summaryrefslogtreecommitdiffstats
path: root/source4/lib/registry
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source4/lib/registry
parentInitial commit. (diff)
downloadsamba-upstream.tar.xz
samba-upstream.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--source4/lib/registry/Doxyfile24
-rw-r--r--source4/lib/registry/README42
-rw-r--r--source4/lib/registry/TODO5
-rw-r--r--source4/lib/registry/hive.c176
-rw-r--r--source4/lib/registry/interface.c298
-rw-r--r--source4/lib/registry/ldb.c1018
-rw-r--r--source4/lib/registry/local.c408
-rw-r--r--source4/lib/registry/man/regdiff.1.xml100
-rw-r--r--source4/lib/registry/man/regpatch.1.xml89
-rw-r--r--source4/lib/registry/man/regshell.1.xml189
-rw-r--r--source4/lib/registry/man/regtree.1.xml101
-rw-r--r--source4/lib/registry/patchfile.c543
-rw-r--r--source4/lib/registry/patchfile_dotreg.c435
-rw-r--r--source4/lib/registry/patchfile_preg.c387
-rw-r--r--source4/lib/registry/pyregistry.c494
-rw-r--r--source4/lib/registry/regf.c2319
-rw-r--r--source4/lib/registry/regf.idl167
-rw-r--r--source4/lib/registry/registry.h532
-rw-r--r--source4/lib/registry/rpc.c579
-rw-r--r--source4/lib/registry/samba.c100
-rw-r--r--source4/lib/registry/tests/diff.c291
-rw-r--r--source4/lib/registry/tests/generic.c179
-rw-r--r--source4/lib/registry/tests/hive.c440
-rw-r--r--source4/lib/registry/tests/registry.c645
-rw-r--r--source4/lib/registry/tools/common.c88
-rw-r--r--source4/lib/registry/tools/regdiff.c183
-rw-r--r--source4/lib/registry/tools/regpatch.c119
-rw-r--r--source4/lib/registry/tools/regshell.c708
-rw-r--r--source4/lib/registry/tools/regtree.c209
-rw-r--r--source4/lib/registry/util.c302
-rw-r--r--source4/lib/registry/wine.c45
-rw-r--r--source4/lib/registry/wscript_build69
32 files changed, 11284 insertions, 0 deletions
diff --git a/source4/lib/registry/Doxyfile b/source4/lib/registry/Doxyfile
new file mode 100644
index 0000000..efc01cd
--- /dev/null
+++ b/source4/lib/registry/Doxyfile
@@ -0,0 +1,24 @@
+PROJECT_NAME = REGISTRY
+OUTPUT_DIRECTORY = apidocs
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+OPTIMIZE_OUTPUT_FOR_C = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+GENERATE_TODOLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+SHOW_USED_FILES = NO
+SHOW_DIRECTORIES = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+INPUT = .
+FILE_PATTERNS = *.c *.h *.dox
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+GENERATE_MAN = YES
+ALWAYS_DETAILED_SEC = YES
+JAVADOC_AUTOBRIEF = YES
diff --git a/source4/lib/registry/README b/source4/lib/registry/README
new file mode 100644
index 0000000..07b2c01
--- /dev/null
+++ b/source4/lib/registry/README
@@ -0,0 +1,42 @@
+This is the registry library. The registry is basically a bunch of
+hives, each of which is loaded from a file. When using a local registry,
+it is possible to specify where hives should be loaded from, etc.
+
+There are separate APIs for accessing the data in a hive and the
+data in the registry itself. Each supports different backends.
+
+The following "full registry" backends are currently provided:
+
+ * Remote (over DCE/RPC)
+ * Local (allows "mounting" hives)
+ * Wine (uses the wine plain-text file)
+
+The following hive backends are supported:
+
+ - ldb
+ - regf (NTUSER.DAT-style files)
+ - rpc (Remote individual hives)
+ - directory
+
+reg_open_samba() loads a set of hives based on smb.conf settings.
+Lines in smb.conf should have the following syntax:
+
+registry:<hivename> = <backend>:<location>
+
+So an example usage could be:
+
+registry:HKEY_CURRENT_USER = regf:NTUSER.DAT
+registry:HKEY_LOCAL_MACHINE = ldb:tdb://registry.tdb
+
+WERR_NOT_SUPPORTED will be returned for all hives that haven't been set.
+
+On Windows the various registry hives are loaded from:
+
+HKEY_CURRENT_CONFIG: %SystemRoot%\System32\Config\System
+HKEY_CURRENT_USER: %Profile%\NTUser.dat
+HKEY_LOCAL_MACHINE\SAM: %SystemRoot%\System32\Config\Sam
+HKEY_LOCAL_MACHINE\Security: %SystemRoot%\System32\Config\Security
+HKEY_LOCAL_MACHINE\Software: %SystemRoot%\System32\Config\Software
+HKEY_LOCAL_MACHINE\System: %SystemRoot%\System32\Config\System
+HKEY_USERS\.DEFAULT: %SystemRoot%\System32\Config\Default
+HKEY_LOCAL_MACHINE\HARDWARE: is autogenerated
diff --git a/source4/lib/registry/TODO b/source4/lib/registry/TODO
new file mode 100644
index 0000000..b5809b8
--- /dev/null
+++ b/source4/lib/registry/TODO
@@ -0,0 +1,5 @@
+- ..\..\, \bla\blie support in regshell
+- finish rpc_server
+
+regshell:
+ - support for security descriptors
diff --git a/source4/lib/registry/hive.c b/source4/lib/registry/hive.c
new file mode 100644
index 0000000..1f0672d
--- /dev/null
+++ b/source4/lib/registry/hive.c
@@ -0,0 +1,176 @@
+
+/*
+ Unix SMB/CIFS implementation.
+ Registry hive interface
+ Copyright (C) Jelmer Vernooij 2003-2007.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+*/
+
+#include "includes.h"
+#include "registry.h"
+#include "system/filesys.h"
+#include "param/param.h"
+
+/** Open a registry file/host/etc */
+_PUBLIC_ WERROR reg_open_hive(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **root)
+{
+ int fd, num;
+ char peek[20];
+
+ fd = open(location, O_RDWR);
+ if (fd == -1) {
+ if (errno == ENOENT)
+ return WERR_FILE_NOT_FOUND;
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ num = read(fd, peek, 20);
+ close(fd);
+ if (num == -1) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if (!strncmp(peek, "regf", 4)) {
+ return reg_open_regf_file(parent_ctx, location, root);
+ } else if (!strncmp(peek, "TDB file", 8)) {
+ return reg_open_ldb_file(parent_ctx, location, session_info,
+ credentials, ev_ctx, lp_ctx, root);
+ }
+
+ return WERR_FILE_NOT_FOUND;
+}
+
+_PUBLIC_ WERROR hive_key_get_info(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname, uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ return key->ops->get_key_info(mem_ctx, key, classname, num_subkeys,
+ num_values, last_change_time,
+ max_subkeynamelen,
+ max_valnamelen, max_valbufsize);
+}
+
+_PUBLIC_ WERROR hive_key_add_name(TALLOC_CTX *ctx,
+ const struct hive_key *parent_key,
+ const char *name, const char *classname,
+ struct security_descriptor *desc,
+ struct hive_key **key)
+{
+ SMB_ASSERT(strchr(name, '\\') == NULL);
+
+ return parent_key->ops->add_key(ctx, parent_key, name, classname,
+ desc, key);
+}
+
+_PUBLIC_ WERROR hive_key_del(TALLOC_CTX *mem_ctx, const struct hive_key *key,
+ const char *name)
+{
+ return key->ops->del_key(mem_ctx, key, name);
+}
+
+_PUBLIC_ WERROR hive_get_key_by_name(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char *name,
+ struct hive_key **subkey)
+{
+ return key->ops->get_key_by_name(mem_ctx, key, name, subkey);
+}
+
+WERROR hive_enum_key(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time)
+{
+ return key->ops->enum_key(mem_ctx, key, idx, name, classname,
+ last_mod_time);
+}
+
+WERROR hive_key_set_value(struct hive_key *key, const char *name, uint32_t type,
+ const DATA_BLOB data)
+{
+ if (key->ops->set_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->set_value(key, name, type, data);
+}
+
+WERROR hive_get_value(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ if (key->ops->get_value_by_name == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->get_value_by_name(mem_ctx, key, name, type, data);
+}
+
+WERROR hive_get_value_by_index(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ if (key->ops->enum_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->enum_value(mem_ctx, key, idx, name, type, data);
+}
+
+WERROR hive_get_sec_desc(TALLOC_CTX *mem_ctx,
+ struct hive_key *key,
+ struct security_descriptor **security)
+{
+ if (key->ops->get_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->get_sec_desc(mem_ctx, key, security);
+}
+
+WERROR hive_set_sec_desc(struct hive_key *key,
+ const struct security_descriptor *security)
+{
+ if (key->ops->set_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->set_sec_desc(key, security);
+}
+
+WERROR hive_key_del_value(TALLOC_CTX *mem_ctx, struct hive_key *key,
+ const char *name)
+{
+ if (key->ops->delete_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->ops->delete_value(mem_ctx, key, name);
+}
+
+WERROR hive_key_flush(struct hive_key *key)
+{
+ if (key->ops->flush_key == NULL)
+ return WERR_OK;
+
+ return key->ops->flush_key(key);
+}
diff --git a/source4/lib/registry/interface.c b/source4/lib/registry/interface.c
new file mode 100644
index 0000000..2900c10
--- /dev/null
+++ b/source4/lib/registry/interface.c
@@ -0,0 +1,298 @@
+/*
+ Unix SMB/CIFS implementation.
+ Transparent registry backend handling
+ Copyright (C) Jelmer Vernooij 2003-2007.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+
+#undef strcasecmp
+
+/**
+ * @file
+ * @brief Main registry functions
+ */
+
+const struct reg_predefined_key reg_predefined_keys[] = {
+ {HKEY_CLASSES_ROOT,"HKEY_CLASSES_ROOT" },
+ {HKEY_CURRENT_USER,"HKEY_CURRENT_USER" },
+ {HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE" },
+ {HKEY_PERFORMANCE_DATA, "HKEY_PERFORMANCE_DATA" },
+ {HKEY_USERS, "HKEY_USERS" },
+ {HKEY_CURRENT_CONFIG, "HKEY_CURRENT_CONFIG" },
+ {HKEY_DYN_DATA, "HKEY_DYN_DATA" },
+ {HKEY_PERFORMANCE_TEXT, "HKEY_PERFORMANCE_TEXT" },
+ {HKEY_PERFORMANCE_NLSTEXT, "HKEY_PERFORMANCE_NLSTEXT" },
+ { 0, NULL }
+};
+
+/** Obtain name of specific hkey. */
+_PUBLIC_ const char *reg_get_predef_name(uint32_t hkey)
+{
+ unsigned int i;
+ for (i = 0; reg_predefined_keys[i].name; i++) {
+ if (reg_predefined_keys[i].handle == hkey)
+ return reg_predefined_keys[i].name;
+ }
+
+ return NULL;
+}
+
+/** Get predefined key by name. */
+_PUBLIC_ WERROR reg_get_predefined_key_by_name(struct registry_context *ctx,
+ const char *name,
+ struct registry_key **key)
+{
+ unsigned int i;
+
+ for (i = 0; reg_predefined_keys[i].name; i++) {
+ if (!strcasecmp(reg_predefined_keys[i].name, name))
+ return reg_get_predefined_key(ctx,
+ reg_predefined_keys[i].handle,
+ key);
+ }
+
+ DEBUG(1, ("No predefined key with name '%s'\n", name));
+
+ return WERR_FILE_NOT_FOUND;
+}
+
+/** Get predefined key by id. */
+_PUBLIC_ WERROR reg_get_predefined_key(struct registry_context *ctx,
+ uint32_t hkey, struct registry_key **key)
+{
+ return ctx->ops->get_predefined_key(ctx, hkey, key);
+}
+
+/**
+ * Open a key
+ * First tries to use the open_key function from the backend
+ * then falls back to get_subkey_by_name and later get_subkey_by_index
+ */
+_PUBLIC_ WERROR reg_open_key(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name, struct registry_key **result)
+{
+ if (parent == NULL) {
+ DEBUG(0, ("Invalid parent key specified for open of '%s'\n",
+ name));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (parent->context->ops->open_key == NULL) {
+ DEBUG(0, ("Registry backend doesn't have open_key!\n"));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ return parent->context->ops->open_key(mem_ctx, parent, name, result);
+}
+
+/**
+ * Get value by index
+ */
+_PUBLIC_ WERROR reg_key_get_value_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ uint32_t idx, const char **name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->enum_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->enum_value(mem_ctx, key, idx, name,
+ type, data);
+}
+
+/**
+ * Get the number of subkeys.
+ */
+_PUBLIC_ WERROR reg_key_get_info(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->get_key_info == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->get_key_info(mem_ctx,
+ key, classname, num_subkeys,
+ num_values, last_change_time,
+ max_subkeynamelen,
+ max_valnamelen, max_valbufsize);
+}
+
+/**
+ * Get subkey by index.
+ */
+_PUBLIC_ WERROR reg_key_get_subkey_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ uint32_t idx, const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->enum_key == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->enum_key(mem_ctx, key, idx, name,
+ keyclass, last_changed_time);
+}
+
+/**
+ * Get value by name.
+ */
+_PUBLIC_ WERROR reg_key_get_value_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->get_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->get_value(mem_ctx, key, name, type, data);
+}
+
+/**
+ * Delete a key.
+ */
+_PUBLIC_ WERROR reg_key_del(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name)
+{
+ if (parent == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (parent->context->ops->delete_key == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return parent->context->ops->delete_key(mem_ctx, parent, name);
+}
+
+/**
+ * Add a key.
+ */
+_PUBLIC_ WERROR reg_key_add_name(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *path, const char *key_class,
+ struct security_descriptor *desc,
+ struct registry_key **newkey)
+{
+ if (parent == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (parent->context->ops->create_key == NULL) {
+ DEBUG(1, ("Backend '%s' doesn't support method add_key\n",
+ parent->context->ops->name));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ return parent->context->ops->create_key(mem_ctx, parent, path,
+ key_class, desc, newkey);
+}
+
+/**
+ * Set a value.
+ */
+_PUBLIC_ WERROR reg_val_set(struct registry_key *key, const char *value,
+ uint32_t type, const DATA_BLOB data)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ /* A 'real' set function has preference */
+ if (key->context->ops->set_value == NULL) {
+ DEBUG(1, ("Backend '%s' doesn't support method set_value\n",
+ key->context->ops->name));
+ return WERR_NOT_SUPPORTED;
+ }
+
+ return key->context->ops->set_value(key, value, type, data);
+}
+
+/**
+ * Get the security descriptor on a key.
+ */
+_PUBLIC_ WERROR reg_get_sec_desc(TALLOC_CTX *ctx,
+ const struct registry_key *key,
+ struct security_descriptor **secdesc)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ /* A 'real' set function has preference */
+ if (key->context->ops->get_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->get_sec_desc(ctx, key, secdesc);
+}
+
+/**
+ * Delete a value.
+ */
+_PUBLIC_ WERROR reg_del_value(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ const char *valname)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->delete_value == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->delete_value(mem_ctx, key, valname);
+}
+
+/**
+ * Flush a key to disk.
+ */
+_PUBLIC_ WERROR reg_key_flush(struct registry_key *key)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->flush_key == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->flush_key(key);
+}
+
+_PUBLIC_ WERROR reg_set_sec_desc(struct registry_key *key,
+ const struct security_descriptor *security)
+{
+ if (key == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ if (key->context->ops->set_sec_desc == NULL)
+ return WERR_NOT_SUPPORTED;
+
+ return key->context->ops->set_sec_desc(key, security);
+}
diff --git a/source4/lib/registry/ldb.c b/source4/lib/registry/ldb.c
new file mode 100644
index 0000000..db383a5
--- /dev/null
+++ b/source4/lib/registry/ldb.c
@@ -0,0 +1,1018 @@
+/*
+ Unix SMB/CIFS implementation.
+ Registry interface
+ Copyright (C) 2004-2007, Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2008-2010, Matthias Dieter Wallnöfer, mdw@samba.org
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "registry.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "ldb_wrap.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "param/param.h"
+#include "lib/util/smb_strtox.h"
+
+#undef strcasecmp
+
+static struct hive_operations reg_backend_ldb;
+
+struct ldb_key_data
+{
+ struct hive_key key;
+ struct ldb_context *ldb;
+ struct ldb_dn *dn;
+ struct ldb_message **subkeys, **values;
+ unsigned int subkey_count, value_count;
+ const char *classname;
+};
+
+static void reg_ldb_unpack_value(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg,
+ const char **name, uint32_t *type,
+ DATA_BLOB *data)
+{
+ const struct ldb_val *val;
+ uint32_t value_type;
+
+ if (name != NULL) {
+ *name = talloc_strdup(mem_ctx,
+ ldb_msg_find_attr_as_string(msg, "value",
+ ""));
+ }
+
+ value_type = ldb_msg_find_attr_as_uint(msg, "type", 0);
+ *type = value_type;
+
+ val = ldb_msg_find_ldb_val(msg, "data");
+
+ switch (value_type)
+ {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ if (val != NULL) {
+ /* The data should be provided as UTF16 string */
+ convert_string_talloc(mem_ctx, CH_UTF8, CH_UTF16,
+ val->data, val->length,
+ (void **)&data->data, &data->length);
+ } else {
+ data->data = NULL;
+ data->length = 0;
+ }
+ break;
+
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN:
+ if (val != NULL) {
+ int error = 0;
+ /* The data is a plain DWORD */
+ uint32_t tmp;
+
+ tmp = smb_strtoul((char *)val->data,
+ NULL,
+ 0,
+ &error,
+ SMB_STR_STANDARD);
+ if (error != 0) {
+ data->data = NULL;
+ data->length = 0;
+ break;
+ }
+ data->data = talloc_size(mem_ctx, sizeof(uint32_t));
+ if (data->data != NULL) {
+ SIVAL(data->data, 0, tmp);
+ }
+ data->length = sizeof(uint32_t);
+ } else {
+ data->data = NULL;
+ data->length = 0;
+ }
+ break;
+
+ case REG_QWORD:
+ if (val != NULL) {
+ int error = 0;
+ /* The data is a plain QWORD */
+ uint64_t tmp;
+
+ tmp = smb_strtoull((char *)val->data,
+ NULL,
+ 0,
+ &error,
+ SMB_STR_STANDARD);
+ if (error != 0) {
+ data->data = NULL;
+ data->length = 0;
+ break;
+ }
+ data->data = talloc_size(mem_ctx, sizeof(uint64_t));
+ if (data->data != NULL) {
+ SBVAL(data->data, 0, tmp);
+ }
+ data->length = sizeof(uint64_t);
+ } else {
+ data->data = NULL;
+ data->length = 0;
+ }
+ break;
+
+ case REG_BINARY:
+ default:
+ if (val != NULL) {
+ data->data = talloc_memdup(mem_ctx, val->data,
+ val->length);
+ data->length = val->length;
+ } else {
+ data->data = NULL;
+ data->length = 0;
+ }
+ break;
+ }
+}
+
+static struct ldb_message *reg_ldb_pack_value(struct ldb_context *ctx,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ uint32_t type, DATA_BLOB data)
+{
+ struct ldb_message *msg;
+ char *name_dup, *type_str;
+ int ret;
+
+ msg = ldb_msg_new(mem_ctx);
+ if (msg == NULL) {
+ return NULL;
+ }
+
+ name_dup = talloc_strdup(msg, name);
+ if (name_dup == NULL) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ ret = ldb_msg_add_string(msg, "value", name_dup);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ switch (type) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ if ((data.length > 0) && (data.data != NULL)) {
+ struct ldb_val *val;
+ bool ret2 = false;
+
+ val = talloc_zero(msg, struct ldb_val);
+ if (val == NULL) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ /* The data is provided as UTF16 string */
+ ret2 = convert_string_talloc(mem_ctx, CH_UTF16, CH_UTF8,
+ (void *)data.data, data.length,
+ (void **)&val->data, &val->length);
+ if (ret2) {
+ ret = ldb_msg_add_value(msg, "data", val, NULL);
+ } else {
+ /* workaround for non-standard data */
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ }
+ } else {
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ }
+ break;
+
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN:
+ if ((data.length > 0) && (data.data != NULL)) {
+ if (data.length == sizeof(uint32_t)) {
+ char *conv_str;
+
+ conv_str = talloc_asprintf(msg, "0x%8.8x",
+ IVAL(data.data, 0));
+ if (conv_str == NULL) {
+ talloc_free(msg);
+ return NULL;
+ }
+ ret = ldb_msg_add_string(msg, "data", conv_str);
+ } else {
+ /* workaround for non-standard data */
+ talloc_free(msg);
+ return NULL;
+ }
+ } else {
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ }
+ break;
+
+ case REG_QWORD:
+ if ((data.length > 0) && (data.data != NULL)) {
+ if (data.length == sizeof(uint64_t)) {
+ char *conv_str;
+
+ conv_str = talloc_asprintf(msg, "0x%16.16llx",
+ (unsigned long long)BVAL(data.data, 0));
+ if (conv_str == NULL) {
+ talloc_free(msg);
+ return NULL;
+ }
+ ret = ldb_msg_add_string(msg, "data", conv_str);
+ } else {
+ /* workaround for non-standard data */
+ talloc_free(msg);
+ return NULL;
+
+ }
+ } else {
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ }
+ break;
+
+ case REG_BINARY:
+ default:
+ if ((data.length > 0) && (data.data != NULL)) {
+ ret = ldb_msg_add_value(msg, "data", &data, NULL);
+ } else {
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ }
+ break;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ type_str = talloc_asprintf(mem_ctx, "%u", type);
+ if (type_str == NULL) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ ret = ldb_msg_add_string(msg, "type", type_str);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(msg);
+ return NULL;
+ }
+
+ return msg;
+}
+
+static char *reg_ldb_escape(TALLOC_CTX *mem_ctx, const char *value)
+{
+ struct ldb_val val;
+
+ val.data = discard_const_p(uint8_t, value);
+ val.length = strlen(value);
+
+ return ldb_dn_escape_value(mem_ctx, val);
+}
+
+static int reg_close_ldb_key(struct ldb_key_data *key)
+{
+ if (key->subkeys != NULL) {
+ talloc_free(key->subkeys);
+ key->subkeys = NULL;
+ }
+
+ if (key->values != NULL) {
+ talloc_free(key->values);
+ key->values = NULL;
+ }
+ return 0;
+}
+
+static struct ldb_dn *reg_path_to_ldb(TALLOC_CTX *mem_ctx,
+ const struct hive_key *from,
+ const char *path, const char *add)
+{
+ struct ldb_dn *ret;
+ char *mypath;
+ char *begin;
+ struct ldb_key_data *kd = talloc_get_type(from, struct ldb_key_data);
+ struct ldb_context *ldb = kd->ldb;
+
+ mypath = talloc_strdup(mem_ctx, path);
+ if (mypath == NULL) {
+ return NULL;
+ }
+
+ ret = ldb_dn_new(mem_ctx, ldb, add);
+ if (!ldb_dn_validate(ret)) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ if (!ldb_dn_add_base(ret, kd->dn)) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ while (mypath[0] != '\0') {
+ begin = strchr(mypath, '\\');
+ if (begin != NULL) {
+ *begin = '\0';
+ }
+
+ if (!ldb_dn_add_child_fmt(ret, "key=%s",
+ reg_ldb_escape(mem_ctx, mypath))) {
+ talloc_free(ret);
+ return NULL;
+ }
+
+ if (begin != NULL) {
+ mypath = begin + 1;
+ } else {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static WERROR cache_subkeys(struct ldb_key_data *kd)
+{
+ struct ldb_context *c = kd->ldb;
+ struct ldb_result *res;
+ int ret;
+
+ ret = ldb_search(c, c, &res, kd->dn, LDB_SCOPE_ONELEVEL,
+ NULL, "(key=*)");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting subkeys for '%s': %s\n",
+ ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ kd->subkey_count = res->count;
+ kd->subkeys = talloc_steal(kd, res->msgs);
+ talloc_free(res);
+
+ return WERR_OK;
+}
+
+static WERROR cache_values(struct ldb_key_data *kd)
+{
+ struct ldb_context *c = kd->ldb;
+ struct ldb_result *res;
+ int ret;
+
+ ret = ldb_search(c, c, &res, kd->dn, LDB_SCOPE_ONELEVEL,
+ NULL, "(value=*)");
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting values for '%s': %s\n",
+ ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ kd->value_count = res->count;
+ kd->values = talloc_steal(kd, res->msgs);
+ talloc_free(res);
+
+ return WERR_OK;
+}
+
+
+static WERROR ldb_get_subkey_by_id(TALLOC_CTX *mem_ctx,
+ const struct hive_key *k, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time)
+{
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+
+ /* Initialization */
+ if (name != NULL)
+ *name = NULL;
+ if (classname != NULL)
+ *classname = NULL;
+ if (last_mod_time != NULL)
+ *last_mod_time = 0; /* TODO: we need to add this to the
+ ldb backend properly */
+
+ /* Do a search if necessary */
+ if (kd->subkeys == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_subkeys(kd));
+ }
+
+ if (idx >= kd->subkey_count)
+ return WERR_NO_MORE_ITEMS;
+
+ if (name != NULL)
+ *name = talloc_strdup(mem_ctx,
+ ldb_msg_find_attr_as_string(kd->subkeys[idx], "key", NULL));
+ if (classname != NULL)
+ *classname = talloc_strdup(mem_ctx,
+ ldb_msg_find_attr_as_string(kd->subkeys[idx], "classname", NULL));
+
+ return WERR_OK;
+}
+
+static WERROR ldb_get_default_value(TALLOC_CTX *mem_ctx,
+ const struct hive_key *k,
+ const char **name, uint32_t *data_type,
+ DATA_BLOB *data)
+{
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+ struct ldb_context *c = kd->ldb;
+ const char* attrs[] = { "data", "type", NULL };
+ struct ldb_result *res;
+ int ret;
+
+ ret = ldb_search(c, mem_ctx, &res, kd->dn, LDB_SCOPE_BASE, attrs,
+ NULL);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting default value for '%s': %s\n",
+ ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ if (res->count == 0 || res->msgs[0]->num_elements == 0) {
+ talloc_free(res);
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if ((data_type != NULL) && (data != NULL)) {
+ reg_ldb_unpack_value(mem_ctx, res->msgs[0], name, data_type,
+ data);
+ }
+
+ talloc_free(res);
+
+ return WERR_OK;
+}
+
+static WERROR ldb_get_value_by_id(TALLOC_CTX *mem_ctx, struct hive_key *k,
+ uint32_t idx, const char **name,
+ uint32_t *data_type, DATA_BLOB *data)
+{
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+
+ /* if the default value exists, give it back */
+ if (W_ERROR_IS_OK(ldb_get_default_value(mem_ctx, k, name, data_type,
+ data))) {
+ if (idx == 0)
+ return WERR_OK;
+ else
+ --idx;
+ }
+
+ /* Do the search if necessary */
+ if (kd->values == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_values(kd));
+ }
+
+ if (idx >= kd->value_count)
+ return WERR_NO_MORE_ITEMS;
+
+ reg_ldb_unpack_value(mem_ctx, kd->values[idx], name, data_type, data);
+
+ return WERR_OK;
+}
+
+static WERROR ldb_get_value(TALLOC_CTX *mem_ctx, struct hive_key *k,
+ const char *name, uint32_t *data_type,
+ DATA_BLOB *data)
+{
+ struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
+ const char *res_name;
+ uint32_t idx;
+
+ /* the default value was requested, give it back */
+ if (name[0] == '\0') {
+ return ldb_get_default_value(mem_ctx, k, NULL, data_type, data);
+ }
+
+ /* Do the search if necessary */
+ if (kd->values == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_values(kd));
+ }
+
+ for (idx = 0; idx < kd->value_count; idx++) {
+ res_name = ldb_msg_find_attr_as_string(kd->values[idx], "value",
+ "");
+ if (ldb_attr_cmp(name, res_name) == 0) {
+ reg_ldb_unpack_value(mem_ctx, kd->values[idx], NULL,
+ data_type, data);
+ return WERR_OK;
+ }
+ }
+
+ return WERR_FILE_NOT_FOUND;
+}
+
+static WERROR ldb_open_key(TALLOC_CTX *mem_ctx, const struct hive_key *h,
+ const char *name, struct hive_key **key)
+{
+ struct ldb_result *res;
+ struct ldb_dn *ldb_path;
+ int ret;
+ struct ldb_key_data *newkd;
+ struct ldb_key_data *kd = talloc_get_type(h, struct ldb_key_data);
+ struct ldb_context *c = kd->ldb;
+
+ ldb_path = reg_path_to_ldb(mem_ctx, h, name, NULL);
+ W_ERROR_HAVE_NO_MEMORY(ldb_path);
+
+ ret = ldb_search(c, mem_ctx, &res, ldb_path, LDB_SCOPE_BASE, NULL,
+ NULL);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(3, ("Error opening key '%s': %s\n",
+ ldb_dn_get_linearized(ldb_path), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ } else if (res->count == 0) {
+ DEBUG(3, ("Key '%s' not found\n",
+ ldb_dn_get_linearized(ldb_path)));
+ talloc_free(res);
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ newkd = talloc_zero(mem_ctx, struct ldb_key_data);
+ W_ERROR_HAVE_NO_MEMORY(newkd);
+ newkd->key.ops = &reg_backend_ldb;
+ newkd->ldb = talloc_reference(newkd, kd->ldb);
+ newkd->dn = ldb_dn_copy(newkd, res->msgs[0]->dn);
+ newkd->classname = talloc_steal(newkd,
+ ldb_msg_find_attr_as_string(res->msgs[0], "classname", NULL));
+
+ talloc_free(res);
+
+ *key = (struct hive_key *)newkd;
+
+ return WERR_OK;
+}
+
+WERROR reg_open_ldb_file(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **k)
+{
+ struct ldb_key_data *kd;
+ struct ldb_context *wrap;
+ struct ldb_message *attrs_msg;
+
+ if (location == NULL)
+ return WERR_INVALID_PARAMETER;
+
+ wrap = ldb_wrap_connect(parent_ctx, ev_ctx, lp_ctx,
+ location, session_info, credentials, 0);
+
+ if (wrap == NULL) {
+ DEBUG(1, (__FILE__": unable to connect\n"));
+ return WERR_FOOBAR;
+ }
+
+ attrs_msg = ldb_msg_new(wrap);
+ W_ERROR_HAVE_NO_MEMORY(attrs_msg);
+ attrs_msg->dn = ldb_dn_new(attrs_msg, wrap, "@ATTRIBUTES");
+ W_ERROR_HAVE_NO_MEMORY(attrs_msg->dn);
+ ldb_msg_add_string(attrs_msg, "key", "CASE_INSENSITIVE");
+ ldb_msg_add_string(attrs_msg, "value", "CASE_INSENSITIVE");
+
+ ldb_add(wrap, attrs_msg);
+
+ ldb_set_debug_stderr(wrap);
+
+ kd = talloc_zero(parent_ctx, struct ldb_key_data);
+ kd->key.ops = &reg_backend_ldb;
+ kd->ldb = talloc_reference(kd, wrap);
+ talloc_set_destructor (kd, reg_close_ldb_key);
+ kd->dn = ldb_dn_new(kd, wrap, "hive=NONE");
+
+ *k = (struct hive_key *)kd;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_add_key(TALLOC_CTX *mem_ctx, const struct hive_key *parent,
+ const char *name, const char *classname,
+ struct security_descriptor *sd,
+ struct hive_key **newkey)
+{
+ struct ldb_key_data *parentkd = discard_const_p(struct ldb_key_data, parent);
+ struct ldb_dn *ldb_path;
+ struct ldb_message *msg;
+ struct ldb_key_data *newkd;
+ int ret;
+
+ ldb_path = reg_path_to_ldb(mem_ctx, parent, name, NULL);
+ W_ERROR_HAVE_NO_MEMORY(ldb_path);
+
+ msg = ldb_msg_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+
+ msg->dn = ldb_path;
+
+ ldb_msg_add_string(msg, "key", name);
+ if (classname != NULL) {
+ ldb_msg_add_string(msg, "classname", classname);
+ }
+
+ ret = ldb_add(parentkd->ldb, msg);
+
+ talloc_free(msg);
+
+ if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
+ return WERR_ALREADY_EXISTS;
+ }
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_add: %s\n", ldb_errstring(parentkd->ldb)));
+ return WERR_FOOBAR;
+ }
+
+ DEBUG(2, ("key added: %s\n", ldb_dn_get_linearized(ldb_path)));
+
+ newkd = talloc_zero(mem_ctx, struct ldb_key_data);
+ W_ERROR_HAVE_NO_MEMORY(newkd);
+ newkd->ldb = talloc_reference(newkd, parentkd->ldb);
+ newkd->key.ops = &reg_backend_ldb;
+ newkd->dn = talloc_steal(newkd, ldb_path);
+ newkd->classname = talloc_steal(newkd, classname);
+
+ *newkey = (struct hive_key *)newkd;
+
+ /* reset cache */
+ talloc_free(parentkd->subkeys);
+ parentkd->subkeys = NULL;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_del_value(TALLOC_CTX *mem_ctx, struct hive_key *key,
+ const char *child)
+{
+ int ret;
+ struct ldb_key_data *kd = talloc_get_type(key, struct ldb_key_data);
+ struct ldb_message *msg;
+ struct ldb_dn *childdn;
+
+ if (child[0] == '\0') {
+ /* default value */
+ msg = ldb_msg_new(mem_ctx);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+ msg->dn = ldb_dn_copy(msg, kd->dn);
+ W_ERROR_HAVE_NO_MEMORY(msg->dn);
+ ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+ if (ret != LDB_SUCCESS) {
+ return WERR_FOOBAR;
+ }
+ ret = ldb_msg_add_empty(msg, "type", LDB_FLAG_MOD_DELETE,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ return WERR_FOOBAR;
+ }
+
+ ret = ldb_modify(kd->ldb, msg);
+
+ talloc_free(msg);
+
+ if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ return WERR_FILE_NOT_FOUND;
+ } else if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_del_value: %s\n", ldb_errstring(kd->ldb)));
+ return WERR_FOOBAR;
+ }
+ } else {
+ /* normal value */
+ childdn = ldb_dn_copy(kd->ldb, kd->dn);
+ if (!ldb_dn_add_child_fmt(childdn, "value=%s",
+ reg_ldb_escape(childdn, child)))
+ {
+ talloc_free(childdn);
+ return WERR_FOOBAR;
+ }
+
+ ret = ldb_delete(kd->ldb, childdn);
+
+ talloc_free(childdn);
+
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ return WERR_FILE_NOT_FOUND;
+ } else if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_del_value: %s\n", ldb_errstring(kd->ldb)));
+ return WERR_FOOBAR;
+ }
+ }
+
+ /* reset cache */
+ talloc_free(kd->values);
+ kd->values = NULL;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_del_key(TALLOC_CTX *mem_ctx, const struct hive_key *key,
+ const char *name)
+{
+ unsigned int i;
+ int ret;
+ struct ldb_key_data *parentkd = talloc_get_type(key, struct ldb_key_data);
+ struct ldb_dn *ldb_path;
+ struct ldb_context *c = parentkd->ldb;
+ struct ldb_result *res_keys;
+ struct ldb_result *res_vals;
+ WERROR werr;
+ struct hive_key *hk;
+
+ /* Verify key exists by opening it */
+ werr = ldb_open_key(mem_ctx, key, name, &hk);
+ if (!W_ERROR_IS_OK(werr)) {
+ return werr;
+ }
+
+ ldb_path = reg_path_to_ldb(mem_ctx, key, name, NULL);
+ W_ERROR_HAVE_NO_MEMORY(ldb_path);
+
+ /* Search for subkeys */
+ ret = ldb_search(c, mem_ctx, &res_keys, ldb_path, LDB_SCOPE_ONELEVEL,
+ NULL, "(key=*)");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting subkeys for '%s': %s\n",
+ ldb_dn_get_linearized(ldb_path), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ /* Search for values */
+ ret = ldb_search(c, mem_ctx, &res_vals, ldb_path, LDB_SCOPE_ONELEVEL,
+ NULL, "(value=*)");
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("Error getting values for '%s': %s\n",
+ ldb_dn_get_linearized(ldb_path), ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ /* Start an explicit transaction */
+ ret = ldb_transaction_start(c);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(0, ("ldb_transaction_start: %s\n", ldb_errstring(c)));
+ return WERR_FOOBAR;
+ }
+
+ if (res_keys->count || res_vals->count)
+ {
+ /* Delete any subkeys */
+ for (i = 0; i < res_keys->count; i++)
+ {
+ werr = ldb_del_key(mem_ctx, hk,
+ ldb_msg_find_attr_as_string(
+ res_keys->msgs[i],
+ "key", NULL));
+ if (!W_ERROR_IS_OK(werr)) {
+ ret = ldb_transaction_cancel(c);
+ return werr;
+ }
+ }
+
+ /* Delete any values */
+ for (i = 0; i < res_vals->count; i++)
+ {
+ werr = ldb_del_value(mem_ctx, hk,
+ ldb_msg_find_attr_as_string(
+ res_vals->msgs[i],
+ "value", NULL));
+ if (!W_ERROR_IS_OK(werr)) {
+ ret = ldb_transaction_cancel(c);
+ return werr;
+ }
+ }
+ }
+ talloc_free(res_keys);
+ talloc_free(res_vals);
+
+ /* Delete the key itself */
+ ret = ldb_delete(c, ldb_path);
+
+ if (ret != LDB_SUCCESS)
+ {
+ DEBUG(1, ("ldb_del_key: %s\n", ldb_errstring(c)));
+ ret = ldb_transaction_cancel(c);
+ return WERR_FOOBAR;
+ }
+
+ /* Commit the transaction */
+ ret = ldb_transaction_commit(c);
+
+ if (ret != LDB_SUCCESS)
+ {
+ DEBUG(0, ("ldb_transaction_commit: %s\n", ldb_errstring(c)));
+ ret = ldb_transaction_cancel(c);
+ return WERR_FOOBAR;
+ }
+
+ /* reset cache */
+ talloc_free(parentkd->subkeys);
+ parentkd->subkeys = NULL;
+
+ return WERR_OK;
+}
+
+static WERROR ldb_set_value(struct hive_key *parent,
+ const char *name, uint32_t type,
+ const DATA_BLOB data)
+{
+ struct ldb_message *msg;
+ struct ldb_key_data *kd = talloc_get_type(parent, struct ldb_key_data);
+ unsigned int i;
+ int ret;
+ TALLOC_CTX *mem_ctx = talloc_init("ldb_set_value");
+
+ msg = reg_ldb_pack_value(kd->ldb, mem_ctx, name, type, data);
+ W_ERROR_HAVE_NO_MEMORY(msg);
+
+ msg->dn = ldb_dn_copy(msg, kd->dn);
+ W_ERROR_HAVE_NO_MEMORY(msg->dn);
+
+ if (name[0] != '\0') {
+ /* For a default value, we add/overwrite the attributes to/of the hive.
+ For a normal value, we create a new child. */
+ if (!ldb_dn_add_child_fmt(msg->dn, "value=%s",
+ reg_ldb_escape(mem_ctx, name)))
+ {
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+ }
+
+ /* Try first a "modify" and if this doesn't work do try an "add" */
+ for (i = 0; i < msg->num_elements; i++) {
+ if (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) != LDB_FLAG_MOD_DELETE) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+ }
+ ret = ldb_modify(kd->ldb, msg);
+ if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+ i = 0;
+ while (i < msg->num_elements) {
+ if (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE) {
+ ldb_msg_remove_element(msg, &msg->elements[i]);
+ } else {
+ ++i;
+ }
+ }
+ ret = ldb_add(kd->ldb, msg);
+ }
+ if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ /* ignore this -> the value didn't exist and also now doesn't */
+ ret = LDB_SUCCESS;
+ }
+
+ talloc_free(msg);
+
+ if (ret != LDB_SUCCESS) {
+ DEBUG(1, ("ldb_set_value: %s\n", ldb_errstring(kd->ldb)));
+ talloc_free(mem_ctx);
+ return WERR_FOOBAR;
+ }
+
+ /* reset cache */
+ talloc_free(kd->values);
+ kd->values = NULL;
+
+ talloc_free(mem_ctx);
+ return WERR_OK;
+}
+
+static WERROR ldb_get_key_info(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ struct ldb_key_data *kd = talloc_get_type(key, struct ldb_key_data);
+ uint32_t default_value_type = REG_NONE;
+ DATA_BLOB default_value = { NULL, 0 };
+ WERROR werr;
+
+ /* Initialization */
+ if (classname != NULL)
+ *classname = NULL;
+ if (num_subkeys != NULL)
+ *num_subkeys = 0;
+ if (num_values != NULL)
+ *num_values = 0;
+ if (last_change_time != NULL)
+ *last_change_time = 0;
+ if (max_subkeynamelen != NULL)
+ *max_subkeynamelen = 0;
+ if (max_valnamelen != NULL)
+ *max_valnamelen = 0;
+ if (max_valbufsize != NULL)
+ *max_valbufsize = 0;
+
+ /* We need this to get the default value (if it exists) for counting
+ * the values under the key and for finding out the longest value buffer
+ * size. If no default value exists the DATA_BLOB "default_value" will
+ * remain { NULL, 0 }. */
+ werr = ldb_get_default_value(mem_ctx, key, NULL, &default_value_type,
+ &default_value);
+ if ((!W_ERROR_IS_OK(werr)) && (!W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND))) {
+ return werr;
+ }
+
+ if (kd->subkeys == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_subkeys(kd));
+ }
+ if (kd->values == NULL) {
+ W_ERROR_NOT_OK_RETURN(cache_values(kd));
+ }
+
+ if (classname != NULL) {
+ *classname = kd->classname;
+ }
+
+ if (num_subkeys != NULL) {
+ *num_subkeys = kd->subkey_count;
+ }
+ if (num_values != NULL) {
+ *num_values = kd->value_count;
+ /* also consider the default value if it exists */
+ if (default_value.data != NULL) {
+ ++(*num_values);
+ }
+ }
+
+
+ if (max_subkeynamelen != NULL) {
+ unsigned int i;
+ struct ldb_message_element *el;
+
+ for (i = 0; i < kd->subkey_count; i++) {
+ el = ldb_msg_find_element(kd->subkeys[i], "key");
+ *max_subkeynamelen = MAX(*max_subkeynamelen, el->values[0].length);
+ }
+ }
+
+ if (max_valnamelen != NULL || max_valbufsize != NULL) {
+ unsigned int i;
+ struct ldb_message_element *el;
+ W_ERROR_NOT_OK_RETURN(cache_values(kd));
+
+ /* also consider the default value if it exists */
+ if ((max_valbufsize != NULL) && (default_value.data != NULL)) {
+ *max_valbufsize = MAX(*max_valbufsize,
+ default_value.length);
+ }
+
+ for (i = 0; i < kd->value_count; i++) {
+ if (max_valnamelen != NULL) {
+ el = ldb_msg_find_element(kd->values[i], "value");
+ *max_valnamelen = MAX(*max_valnamelen, el->values[0].length);
+ }
+
+ if (max_valbufsize != NULL) {
+ uint32_t data_type;
+ DATA_BLOB data;
+ reg_ldb_unpack_value(mem_ctx,
+ kd->values[i], NULL,
+ &data_type, &data);
+ *max_valbufsize = MAX(*max_valbufsize, data.length);
+ talloc_free(data.data);
+ }
+ }
+ }
+
+ talloc_free(default_value.data);
+
+ return WERR_OK;
+}
+
+static struct hive_operations reg_backend_ldb = {
+ .name = "ldb",
+ .add_key = ldb_add_key,
+ .del_key = ldb_del_key,
+ .get_key_by_name = ldb_open_key,
+ .enum_value = ldb_get_value_by_id,
+ .enum_key = ldb_get_subkey_by_id,
+ .set_value = ldb_set_value,
+ .get_value_by_name = ldb_get_value,
+ .delete_value = ldb_del_value,
+ .get_key_info = ldb_get_key_info,
+};
diff --git a/source4/lib/registry/local.c b/source4/lib/registry/local.c
new file mode 100644
index 0000000..4b00953
--- /dev/null
+++ b/source4/lib/registry/local.c
@@ -0,0 +1,408 @@
+/*
+ Unix SMB/CIFS implementation.
+ Transparent registry backend handling
+ Copyright (C) Jelmer Vernooij 2003-2007.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+
+struct reg_key_path {
+ uint32_t predefined_key;
+ const char **elements;
+};
+
+struct registry_local {
+ const struct registry_operations *ops;
+
+ struct mountpoint {
+ struct reg_key_path path;
+ struct hive_key *key;
+ struct mountpoint *prev, *next;
+ } *mountpoints;
+};
+
+struct local_key {
+ struct registry_key global;
+ struct reg_key_path path;
+ struct hive_key *hive_key;
+};
+
+
+struct registry_key *reg_import_hive_key(struct registry_context *ctx,
+ struct hive_key *hive,
+ uint32_t predefined_key,
+ const char **elements)
+{
+ struct local_key *local_key;
+ struct reg_key_path parent_path;
+
+ parent_path.predefined_key = predefined_key;
+ parent_path.elements = elements;
+
+ local_key = talloc(ctx, struct local_key);
+ if (local_key != NULL) {
+ local_key->hive_key = talloc_reference(local_key, hive);
+ local_key->global.context = talloc_reference(local_key, ctx);
+ local_key->path = parent_path;
+ }
+
+ return (struct registry_key *)local_key;
+}
+
+
+static WERROR local_open_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *path,
+ struct registry_key **result)
+{
+ char *orig, *curbegin, *curend;
+ struct local_key *local_parent = talloc_get_type(parent,
+ struct local_key);
+ struct hive_key *curkey = local_parent->hive_key;
+ WERROR error;
+ const char **elements = NULL;
+ int el;
+
+ if (path == NULL || path[0] == '\0') {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ orig = talloc_strdup(mem_ctx, path);
+ W_ERROR_HAVE_NO_MEMORY(orig);
+ curbegin = orig;
+ curend = strchr(orig, '\\');
+
+ if (local_parent->path.elements != NULL) {
+ elements = talloc_array(mem_ctx, const char *,
+ str_list_length(local_parent->path.elements) + 1);
+ W_ERROR_HAVE_NO_MEMORY(elements);
+ for (el = 0; local_parent->path.elements[el] != NULL; el++) {
+ elements[el] = talloc_reference(elements,
+ local_parent->path.elements[el]);
+ }
+ elements[el] = NULL;
+ } else {
+ elements = NULL;
+ el = 0;
+ }
+
+ do {
+ if (curend != NULL)
+ *curend = '\0';
+ elements = talloc_realloc(mem_ctx, elements, const char *, el+2);
+ W_ERROR_HAVE_NO_MEMORY(elements);
+ elements[el] = talloc_strdup(elements, curbegin);
+ W_ERROR_HAVE_NO_MEMORY(elements[el]);
+ el++;
+ elements[el] = NULL;
+ error = hive_get_key_by_name(mem_ctx, curkey,
+ curbegin, &curkey);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(2, ("Opening key %s failed: %s\n", curbegin,
+ win_errstr(error)));
+ talloc_free(orig);
+ return error;
+ }
+ if (curend == NULL)
+ break;
+ curbegin = curend + 1;
+ curend = strchr(curbegin, '\\');
+ } while (curbegin[0] != '\0');
+ talloc_free(orig);
+
+ *result = reg_import_hive_key(local_parent->global.context, curkey,
+ local_parent->path.predefined_key,
+ talloc_steal(curkey, elements));
+
+ return WERR_OK;
+}
+
+WERROR local_get_predefined_key(struct registry_context *ctx,
+ uint32_t key_id, struct registry_key **key)
+{
+ struct registry_local *rctx = talloc_get_type(ctx,
+ struct registry_local);
+ struct mountpoint *mp;
+
+ for (mp = rctx->mountpoints; mp != NULL; mp = mp->next) {
+ if (mp->path.predefined_key == key_id &&
+ mp->path.elements == NULL)
+ break;
+ }
+
+ if (mp == NULL)
+ return WERR_FILE_NOT_FOUND;
+
+ *key = reg_import_hive_key(ctx, mp->key,
+ mp->path.predefined_key,
+ mp->path.elements);
+
+ return WERR_OK;
+}
+
+static WERROR local_enum_key(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_enum_key(mem_ctx, local->hive_key, idx, name, keyclass,
+ last_changed_time);
+}
+
+static WERROR local_create_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *path,
+ const char *key_class,
+ struct security_descriptor *security,
+ struct registry_key **result)
+{
+ char *orig, *curbegin, *curend;
+ struct local_key *local_parent = talloc_get_type(parent,
+ struct local_key);
+ struct hive_key *curkey = local_parent->hive_key;
+ WERROR error;
+ const char **elements = NULL;
+ int el;
+
+ if (path == NULL || path[0] == '\0') {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ orig = talloc_strdup(mem_ctx, path);
+ W_ERROR_HAVE_NO_MEMORY(orig);
+ curbegin = orig;
+ curend = strchr(orig, '\\');
+
+ if (local_parent->path.elements != NULL) {
+ elements = talloc_array(mem_ctx, const char *,
+ str_list_length(local_parent->path.elements) + 1);
+ W_ERROR_HAVE_NO_MEMORY(elements);
+ for (el = 0; local_parent->path.elements[el] != NULL; el++) {
+ elements[el] = talloc_reference(elements,
+ local_parent->path.elements[el]);
+ }
+ elements[el] = NULL;
+ } else {
+ elements = NULL;
+ el = 0;
+ }
+
+ do {
+ if (curend != NULL)
+ *curend = '\0';
+ elements = talloc_realloc(mem_ctx, elements, const char *, el+2);
+ W_ERROR_HAVE_NO_MEMORY(elements);
+ elements[el] = talloc_strdup(elements, curbegin);
+ W_ERROR_HAVE_NO_MEMORY(elements[el]);
+ el++;
+ elements[el] = NULL;
+ error = hive_get_key_by_name(mem_ctx, curkey,
+ curbegin, &curkey);
+ if (W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND)) {
+ error = hive_key_add_name(mem_ctx, curkey, curbegin,
+ key_class, security,
+ &curkey);
+ }
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(2, ("Open/Creation of key %s failed: %s\n",
+ curbegin, win_errstr(error)));
+ talloc_free(orig);
+ return error;
+ }
+ if (curend == NULL)
+ break;
+ curbegin = curend + 1;
+ curend = strchr(curbegin, '\\');
+ } while (curbegin[0] != '\0');
+ talloc_free(orig);
+
+ *result = reg_import_hive_key(local_parent->global.context, curkey,
+ local_parent->path.predefined_key,
+ talloc_steal(curkey, elements));
+
+ return WERR_OK;
+}
+
+static WERROR local_set_value(struct registry_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data)
+{
+ struct local_key *local = (struct local_key *)key;
+
+ if (name == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return hive_key_set_value(local->hive_key, name, type, data);
+}
+
+static WERROR local_get_value(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name, uint32_t *type, DATA_BLOB *data)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ if (name == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return hive_get_value(mem_ctx, local->hive_key, name, type, data);
+}
+
+static WERROR local_enum_value(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_get_value_by_index(mem_ctx, local->hive_key, idx,
+ name, type, data);
+}
+
+static WERROR local_delete_key(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ const char *name)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ if (name == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return hive_key_del(mem_ctx, local->hive_key, name);
+}
+
+static WERROR local_delete_value(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ const char *name)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ if (name == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return hive_key_del_value(mem_ctx, local->hive_key, name);
+}
+
+static WERROR local_flush_key(struct registry_key *key)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_key_flush(local->hive_key);
+}
+
+static WERROR local_get_key_info(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_key_get_info(mem_ctx, local->hive_key,
+ classname, num_subkeys, num_values,
+ last_change_time, max_subkeynamelen,
+ max_valnamelen, max_valbufsize);
+}
+static WERROR local_get_sec_desc(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ struct security_descriptor **security)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_get_sec_desc(mem_ctx, local->hive_key, security);
+}
+static WERROR local_set_sec_desc(struct registry_key *key,
+ const struct security_descriptor *security)
+{
+ const struct local_key *local = (const struct local_key *)key;
+
+ return hive_set_sec_desc(local->hive_key, security);
+}
+const static struct registry_operations local_ops = {
+ .name = "local",
+ .open_key = local_open_key,
+ .get_predefined_key = local_get_predefined_key,
+ .enum_key = local_enum_key,
+ .create_key = local_create_key,
+ .set_value = local_set_value,
+ .get_value = local_get_value,
+ .enum_value = local_enum_value,
+ .delete_key = local_delete_key,
+ .delete_value = local_delete_value,
+ .flush_key = local_flush_key,
+ .get_key_info = local_get_key_info,
+ .get_sec_desc = local_get_sec_desc,
+ .set_sec_desc = local_set_sec_desc,
+};
+
+WERROR reg_open_local(TALLOC_CTX *mem_ctx, struct registry_context **ctx)
+{
+ struct registry_local *ret = talloc_zero(mem_ctx,
+ struct registry_local);
+
+ W_ERROR_HAVE_NO_MEMORY(ret);
+
+ ret->ops = &local_ops;
+
+ *ctx = (struct registry_context *)ret;
+
+ return WERR_OK;
+}
+
+WERROR reg_mount_hive(struct registry_context *rctx,
+ struct hive_key *hive_key,
+ uint32_t key_id,
+ const char **elements)
+{
+ struct registry_local *reg_local = talloc_get_type(rctx,
+ struct registry_local);
+ struct mountpoint *mp;
+ unsigned int i = 0;
+
+ mp = talloc(rctx, struct mountpoint);
+ W_ERROR_HAVE_NO_MEMORY(mp);
+ mp->path.predefined_key = key_id;
+ mp->prev = mp->next = NULL;
+ mp->key = hive_key;
+ if (elements != NULL && elements[0] != NULL) {
+ mp->path.elements = talloc_array(mp, const char *,
+ str_list_length(elements));
+ W_ERROR_HAVE_NO_MEMORY(mp->path.elements);
+ for (i = 0; elements[i] != NULL; i++) {
+ mp->path.elements[i] = talloc_reference(mp->path.elements,
+ elements[i]);
+ }
+ mp->path.elements[i] = NULL;
+ } else {
+ mp->path.elements = NULL;
+ }
+
+ DLIST_ADD(reg_local->mountpoints, mp);
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/man/regdiff.1.xml b/source4/lib/registry/man/regdiff.1.xml
new file mode 100644
index 0000000..23aae34
--- /dev/null
+++ b/source4/lib/registry/man/regdiff.1.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="regdiff.1">
+
+<refmeta>
+ <refentrytitle>regdiff</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="source">Samba</refmiscinfo>
+ <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+ <refmiscinfo class="version">4.0</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regdiff</refname>
+ <refpurpose>Diff program for Windows registry files</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regdiff</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>regdiff compares two Windows registry files key by key
+ and value by value and generates a text file that contains the
+ differences between the two files.</para>
+
+ <para>A file generated by regdiff can later be applied to a
+ registry file by the regpatch utility. </para>
+
+ <para>regdiff and regpatch use the same file format as
+ the regedit32.exe utility from Windows.</para>
+
+</refsect1>
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para>
+ <para>
+ This argument can be specified twice: once for the first
+ registry file and once for the second.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.
+ </para>
+ <para>
+ This argument can be specified twice: once for the first
+ registry file and once for the second.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>gregedit, regshell, regpatch, regtree, samba, patch, diff</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regdiff were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/man/regpatch.1.xml b/source4/lib/registry/man/regpatch.1.xml
new file mode 100644
index 0000000..3a15082
--- /dev/null
+++ b/source4/lib/registry/man/regpatch.1.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="regpatch.1">
+
+<refmeta>
+ <refentrytitle>regpatch</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="source">Samba</refmiscinfo>
+ <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+ <refmiscinfo class="version">4.0</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regpatch</refname>
+ <refpurpose>Applies registry patches to registry files</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regpatch</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ <arg choice="opt">patch-file</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>The regpatch utility applies registry patches to Windows registry
+ files. The patch files should have the same format as is being used
+ by the regdiff utility and regedit32.exe from Windows.</para>
+
+ <para>If no patch file is specified on the command line,
+ regpatch attempts to read it from standard input.</para>
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>regdiff, regtree, regshell, gregedit, samba, diff, patch</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regpatch were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/man/regshell.1.xml b/source4/lib/registry/man/regshell.1.xml
new file mode 100644
index 0000000..4653fbb
--- /dev/null
+++ b/source4/lib/registry/man/regshell.1.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="regshell.1">
+
+<refmeta>
+ <refentrytitle>regshell</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="source">Samba</refmiscinfo>
+ <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+ <refmiscinfo class="version">4.0</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regshell</refname>
+ <refpurpose>Windows registry file browser using readline</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regshell</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>
+ regshell is a utility that lets you browse thru a Windows registry
+ file as if you were using a regular unix shell to browse thru a
+ file system.
+ </para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>COMMANDS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>ck|cd &lt;keyname&gt;</term>
+ <listitem><para>
+ Go to the specified subkey.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>ch|predef [predefined-key-name]</term>
+ <listitem><para>
+ Go to the specified predefined key.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>list|ls</term>
+ <listitem><para>
+ List subkeys and values of the current key.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>mkkey|mkdir &lt;keyname&gt;</term>
+ <listitem><para>
+ Create a key with the specified <replaceable>keyname</replaceable> as a subkey of the current key.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>rmval|rm &lt;valname&gt;</term>
+ <listitem><para>
+ Delete the specified value.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>rmkey|rmdir &lt;keyname&gt;</term>
+ <listitem><para>
+ Delete the specified subkey recursively.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>pwd|pwk</term>
+ <listitem><para>Print the full name of the current key.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>set|update</term>
+ <listitem><para>Update the value of a key value. Not implemented at the moment.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>help|?</term>
+ <listitem><para>Print a list of available commands.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>exit|quit</term>
+ <listitem><para>Leave regshell.</para></listitem>
+ </varlistentry>
+ </variablelist>
+</refsect1>
+
+<refsect1>
+ <title>EXAMPLES</title>
+
+ <para>Browsing thru a nt4 registry file</para>
+ <screen>
+<userinput>regshell -b nt4 NTUSER.DAT</userinput>
+$$$PROTO.HIV> <userinput>ls</userinput>
+K AppEvents
+K Console
+K Control Panel
+K Environment
+K Identities
+K Keyboard Layout
+K Network
+K Printers
+K Software
+K UNICODE Program Groups
+K Windows 3.1 Migration Status
+$$$PROTO.HIV> <userinput>exit</userinput>
+</screen>
+
+<para>Listing the subkeys of HKEY_CURRENT_USER\AppEvents on a remote computer:</para>
+<screen>
+<userinput>regshell --remote=ncacn_np:aurelia -c "jelmer%secret"</userinput>
+HKEY_CURRENT_MACHINE> <userinput>predef HKEY_CURRENT_USER</userinput>
+HKEY_CURRENT_USER> <userinput>cd AppEvents</userinput>
+Current path is: HKEY_CURRENT_USER\AppEvents
+HKEY_CURRENT_USER\AppEvents> <userinput>ls</userinput>
+K EventLabels
+K Schemes
+HKEY_CURRENT_USER\AppEvents> <userinput>exit</userinput>
+</screen>
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>regtree, regdiff, regpatch, gregedit, samba</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regshell were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/man/regtree.1.xml b/source4/lib/registry/man/regtree.1.xml
new file mode 100644
index 0000000..0d798e4
--- /dev/null
+++ b/source4/lib/registry/man/regtree.1.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<refentry id="regtree.1">
+
+<refmeta>
+ <refentrytitle>regtree</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="source">Samba</refmiscinfo>
+ <refmiscinfo class="manual">System Administration tools</refmiscinfo>
+ <refmiscinfo class="version">4.0</refmiscinfo>
+</refmeta>
+
+
+<refnamediv>
+ <refname>regtree</refname>
+ <refpurpose>Text-mode registry viewer</refpurpose>
+</refnamediv>
+
+<refsynopsisdiv>
+ <cmdsynopsis>
+ <command>regtree</command>
+ <arg choice="opt">--help</arg>
+ <arg choice="opt">--backend=BACKEND</arg>
+ <arg choice="opt">--fullpath</arg>
+ <arg choice="opt">--no-values</arg>
+ <arg choice="opt">--credentials=CREDENTIALS</arg>
+ <arg choice="opt">location</arg>
+ </cmdsynopsis>
+</refsynopsisdiv>
+
+<refsect1>
+ <title>DESCRIPTION</title>
+
+ <para>The regtree utility prints out all the contents of a
+ Windows registry file. Subkeys are printed with one level
+ more indentation than their parents. </para>
+
+</refsect1>
+
+
+<refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>--help</term>
+ <listitem><para>
+ Show list of available options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--backend BACKEND</term>
+ <listitem><para>Name of backend to load. Possible values are:
+ creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--credentials=CREDENTIALS</term>
+ <listitem><para>
+ Credentials to use, if any. Password should be separated from user name by a percent sign.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>--fullpath</term>
+ <listitem><para>
+ Print the full path to each key instead of only its name.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry><term>--no-values</term>
+ <listitem><para>Don't print values, just keys.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+</refsect1>
+
+<refsect1>
+ <title>VERSION</title>
+
+ <para>This man page is correct for version 4.0 of the Samba suite.</para>
+</refsect1>
+
+<refsect1>
+ <title>SEE ALSO</title>
+
+ <para>gregedit, regshell, regdiff, regpatch, samba</para>
+
+</refsect1>
+
+<refsect1>
+ <title>AUTHOR</title>
+
+ <para>This utility is part of the <ulink url="http://www.samba.org/">Samba</ulink> suite, which is developed by the global <ulink url="http://www.samba.org/samba/team/">Samba Team</ulink>.</para>
+
+ <para>This manpage and regtree were written by Jelmer Vernooij. </para>
+
+</refsect1>
+
+</refentry>
diff --git a/source4/lib/registry/patchfile.c b/source4/lib/registry/patchfile.c
new file mode 100644
index 0000000..8069ed7
--- /dev/null
+++ b/source4/lib/registry/patchfile.c
@@ -0,0 +1,543 @@
+/*
+ Unix SMB/CIFS implementation.
+ Reading registry patch files
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Wilco Baan Hofman 2006
+ Copyright (C) Matthias Dieter Wallnöfer 2008-2010
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+
+
+_PUBLIC_ WERROR reg_preg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+_PUBLIC_ WERROR reg_dotreg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+/*
+ * Generate difference between two keys
+ */
+WERROR reg_generate_diff_key(struct registry_key *oldkey,
+ struct registry_key *newkey,
+ const char *path,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ unsigned int i;
+ struct registry_key *t1 = NULL, *t2 = NULL;
+ char *tmppath;
+ const char *keyname1;
+ WERROR error, error1, error2;
+ TALLOC_CTX *mem_ctx = talloc_init("writediff");
+ uint32_t old_num_subkeys, old_num_values,
+ new_num_subkeys, new_num_values;
+
+ if (oldkey != NULL) {
+ error = reg_key_get_info(mem_ctx, oldkey, NULL,
+ &old_num_subkeys, &old_num_values,
+ NULL, NULL, NULL, NULL);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error occurred while getting key info: %s\n",
+ win_errstr(error)));
+ talloc_free(mem_ctx);
+ return error;
+ }
+ } else {
+ old_num_subkeys = 0;
+ old_num_values = 0;
+ }
+
+ /* Subkeys that were changed or deleted */
+ for (i = 0; i < old_num_subkeys; i++) {
+ error1 = reg_key_get_subkey_by_index(mem_ctx, oldkey, i,
+ &keyname1, NULL, NULL);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
+ win_errstr(error1)));
+ continue;
+ }
+
+ if (newkey != NULL) {
+ error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
+ } else {
+ error2 = WERR_FILE_NOT_FOUND;
+ t2 = NULL;
+ }
+
+ if (!W_ERROR_IS_OK(error2) && !W_ERROR_EQUAL(error2, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ /* if "error2" is going to be "WERR_FILE_NOT_FOUND", then newkey */
+ /* didn't have such a subkey and therefore add a del diff */
+ tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
+ if (tmppath == NULL) {
+ DEBUG(0, ("Out of memory\n"));
+ talloc_free(mem_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ if (!W_ERROR_IS_OK(error2))
+ callbacks->del_key(callback_data, tmppath);
+
+ /* perform here also the recursive invocation */
+ error1 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+ reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
+
+ talloc_free(tmppath);
+ }
+
+ if (newkey != NULL) {
+ error = reg_key_get_info(mem_ctx, newkey, NULL,
+ &new_num_subkeys, &new_num_values,
+ NULL, NULL, NULL, NULL);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error occurred while getting key info: %s\n",
+ win_errstr(error)));
+ talloc_free(mem_ctx);
+ return error;
+ }
+ } else {
+ new_num_subkeys = 0;
+ new_num_values = 0;
+ }
+
+ /* Subkeys that were added */
+ for(i = 0; i < new_num_subkeys; i++) {
+ error1 = reg_key_get_subkey_by_index(mem_ctx, newkey, i,
+ &keyname1, NULL, NULL);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+
+ if (oldkey != NULL) {
+ error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
+
+ if (W_ERROR_IS_OK(error2))
+ continue;
+ } else {
+ error2 = WERR_FILE_NOT_FOUND;
+ t1 = NULL;
+ }
+
+ if (!W_ERROR_EQUAL(error2, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ /* oldkey didn't have such a subkey, add a add diff */
+ tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
+ if (tmppath == NULL) {
+ DEBUG(0, ("Out of memory\n"));
+ talloc_free(mem_ctx);
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ callbacks->add_key(callback_data, tmppath);
+
+ /* perform here also the recursive invocation */
+ error1 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+ reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
+
+ talloc_free(tmppath);
+ }
+
+ /* Values that were added or changed */
+ for(i = 0; i < new_num_values; i++) {
+ const char *name;
+ uint32_t type1, type2;
+ DATA_BLOB contents1 = { NULL, 0 }, contents2 = { NULL, 0 };
+
+ error1 = reg_key_get_value_by_index(mem_ctx, newkey, i,
+ &name, &type1, &contents1);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Unable to get value by index: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+
+ if (oldkey != NULL) {
+ error2 = reg_key_get_value_by_name(mem_ctx, oldkey,
+ name, &type2,
+ &contents2);
+ } else
+ error2 = WERR_FILE_NOT_FOUND;
+
+ if (!W_ERROR_IS_OK(error2)
+ && !W_ERROR_EQUAL(error2, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error occurred while getting value by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ if (W_ERROR_IS_OK(error2)
+ && (data_blob_cmp(&contents1, &contents2) == 0)
+ && (type1 == type2)) {
+ talloc_free(discard_const_p(char, name));
+ talloc_free(contents1.data);
+ talloc_free(contents2.data);
+ continue;
+ }
+
+ callbacks->set_value(callback_data, path, name,
+ type1, contents1);
+
+ talloc_free(discard_const_p(char, name));
+ talloc_free(contents1.data);
+ talloc_free(contents2.data);
+ }
+
+ /* Values that were deleted */
+ for (i = 0; i < old_num_values; i++) {
+ const char *name;
+ uint32_t type;
+ DATA_BLOB contents = { NULL, 0 };
+
+ error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &name,
+ &type, &contents);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Unable to get value by index: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+
+ if (newkey != NULL)
+ error2 = reg_key_get_value_by_name(mem_ctx, newkey,
+ name, &type, &contents);
+ else
+ error2 = WERR_FILE_NOT_FOUND;
+
+ if (W_ERROR_IS_OK(error2)) {
+ talloc_free(discard_const_p(char, name));
+ talloc_free(contents.data);
+ continue;
+ }
+
+ if (!W_ERROR_EQUAL(error2, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error occurred while getting value by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ callbacks->del_value(callback_data, path, name);
+
+ talloc_free(discard_const_p(char, name));
+ talloc_free(contents.data);
+ }
+
+ talloc_free(mem_ctx);
+ return WERR_OK;
+}
+
+/**
+ * Generate diff between two registry contexts
+ */
+_PUBLIC_ WERROR reg_generate_diff(struct registry_context *ctx1,
+ struct registry_context *ctx2,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ unsigned int i;
+ WERROR error;
+
+ for (i = 0; reg_predefined_keys[i].name; i++) {
+ struct registry_key *r1 = NULL, *r2 = NULL;
+
+ error = reg_get_predefined_key(ctx1,
+ reg_predefined_keys[i].handle, &r1);
+ if (!W_ERROR_IS_OK(error) &&
+ !W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Unable to open hive %s for backend 1\n",
+ reg_predefined_keys[i].name));
+ continue;
+ }
+
+ error = reg_get_predefined_key(ctx2,
+ reg_predefined_keys[i].handle, &r2);
+ if (!W_ERROR_IS_OK(error) &&
+ !W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Unable to open hive %s for backend 2\n",
+ reg_predefined_keys[i].name));
+ continue;
+ }
+
+ /* if "r1" is NULL (old hive) and "r2" isn't (new hive) then
+ * the hive doesn't exist yet and we have to generate an add
+ * diff */
+ if ((r1 == NULL) && (r2 != NULL)) {
+ callbacks->add_key(callback_data,
+ reg_predefined_keys[i].name);
+ }
+ /* if "r1" isn't NULL (old hive) and "r2" is (new hive) then
+ * the hive shouldn't exist anymore and we have to generate a
+ * del diff */
+ if ((r1 != NULL) && (r2 == NULL)) {
+ callbacks->del_key(callback_data,
+ reg_predefined_keys[i].name);
+ }
+
+ error = reg_generate_diff_key(r1, r2,
+ reg_predefined_keys[i].name, callbacks,
+ callback_data);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Unable to determine diff: %s\n",
+ win_errstr(error)));
+ return error;
+ }
+ }
+ if (callbacks->done != NULL) {
+ callbacks->done(callback_data);
+ }
+ return WERR_OK;
+}
+
+/**
+ * Load diff file
+ */
+_PUBLIC_ WERROR reg_diff_load(const char *filename,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ int fd;
+ char hdr[4];
+
+ fd = open(filename, O_RDONLY, 0);
+ if (fd == -1) {
+ DEBUG(0, ("Error opening registry patch file `%s'\n",
+ filename));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (read(fd, &hdr, 4) != 4) {
+ DEBUG(0, ("Error reading registry patch file `%s'\n",
+ filename));
+ close(fd);
+ return WERR_GEN_FAILURE;
+ }
+
+ /* Reset position in file */
+ lseek(fd, 0, SEEK_SET);
+#if 0 /* These backends are not supported yet. */
+ if (strncmp(hdr, "CREG", 4) == 0) {
+ /* Must be a W9x CREG Config.pol file */
+ return reg_creg_diff_load(diff, fd);
+ } else if (strncmp(hdr, "regf", 4) == 0) {
+ /* Must be a REGF NTConfig.pol file */
+ return reg_regf_diff_load(diff, fd);
+ } else
+#endif
+ if (strncmp(hdr, "PReg", 4) == 0) {
+ /* Must be a GPO Registry.pol file */
+ return reg_preg_diff_load(fd, callbacks, callback_data);
+ } else {
+ /* Must be a normal .REG file */
+ return reg_dotreg_diff_load(fd, callbacks, callback_data);
+ }
+}
+
+/**
+ * The reg_diff_apply functions
+ */
+static WERROR reg_diff_apply_add_key(void *_ctx, const char *key_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *tmp;
+ char *buf, *buf_ptr;
+ WERROR error;
+
+ /* Recursively create the path */
+ buf = talloc_strdup(ctx, key_name);
+ W_ERROR_HAVE_NO_MEMORY(buf);
+ buf_ptr = buf;
+
+ while (*buf_ptr++ != '\0' ) {
+ if (*buf_ptr == '\\') {
+ *buf_ptr = '\0';
+ error = reg_key_add_abs(ctx, ctx, buf, 0, NULL, &tmp);
+
+ if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
+ !W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error adding new key '%s': %s\n",
+ key_name, win_errstr(error)));
+ return error;
+ }
+ *buf_ptr++ = '\\';
+ talloc_free(tmp);
+ }
+ }
+
+ talloc_free(buf);
+
+ /* Add the key */
+ error = reg_key_add_abs(ctx, ctx, key_name, 0, NULL, &tmp);
+
+ if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
+ !W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error adding new key '%s': %s\n",
+ key_name, win_errstr(error)));
+ return error;
+ }
+ talloc_free(tmp);
+
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+
+ /* We can't proof here for success, because a common superkey could */
+ /* have been deleted before the subkey's (diff order). This removed */
+ /* therefore all children recursively and the "WERR_FILE_NOT_FOUND" result is */
+ /* expected. */
+
+ reg_key_del_abs(ctx, key_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_set_value(void *_ctx, const char *path,
+ const char *value_name,
+ uint32_t value_type, DATA_BLOB value)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *tmp;
+ WERROR error;
+
+ /* Open key */
+ error = reg_open_key_abs(ctx, ctx, path, &tmp);
+
+ if (W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error opening key '%s'\n", path));
+ return error;
+ }
+
+ /* Set value */
+ error = reg_val_set(tmp, value_name,
+ value_type, value);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error setting value '%s'\n", value_name));
+ return error;
+ }
+
+ talloc_free(tmp);
+
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_del_value(void *_ctx, const char *key_name,
+ const char *value_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *tmp;
+ WERROR error;
+
+ /* Open key */
+ error = reg_open_key_abs(ctx, ctx, key_name, &tmp);
+
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error opening key '%s'\n", key_name));
+ return error;
+ }
+
+ error = reg_del_value(ctx, tmp, value_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error deleting value '%s'\n", value_name));
+ return error;
+ }
+
+ talloc_free(tmp);
+
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_del_all_values(void *_ctx, const char *key_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ struct registry_key *key;
+ WERROR error;
+ const char *value_name;
+
+ error = reg_open_key_abs(ctx, ctx, key_name, &key);
+
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error opening key '%s'\n", key_name));
+ return error;
+ }
+
+ W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx, key, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL));
+
+ while (W_ERROR_IS_OK(reg_key_get_value_by_index(
+ ctx, key, 0, &value_name, NULL, NULL))) {
+ error = reg_del_value(ctx, key, value_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error deleting value '%s'\n", value_name));
+ return error;
+ }
+ talloc_free(discard_const_p(char, value_name));
+ }
+
+ talloc_free(key);
+
+ return WERR_OK;
+}
+
+/**
+ * Apply diff to a registry context
+ */
+_PUBLIC_ WERROR reg_diff_apply(struct registry_context *ctx,
+ const char *filename)
+{
+ struct reg_diff_callbacks callbacks;
+
+ callbacks.add_key = reg_diff_apply_add_key;
+ callbacks.del_key = reg_diff_apply_del_key;
+ callbacks.set_value = reg_diff_apply_set_value;
+ callbacks.del_value = reg_diff_apply_del_value;
+ callbacks.del_all_values = reg_diff_apply_del_all_values;
+ callbacks.done = NULL;
+
+ return reg_diff_load(filename, &callbacks, ctx);
+}
diff --git a/source4/lib/registry/patchfile_dotreg.c b/source4/lib/registry/patchfile_dotreg.c
new file mode 100644
index 0000000..5fb342b
--- /dev/null
+++ b/source4/lib/registry/patchfile_dotreg.c
@@ -0,0 +1,435 @@
+/*
+ Unix SMB/CIFS implementation.
+ Reading .REG files
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Wilco Baan Hofman 2006-2010
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+*/
+
+/* FIXME:
+ * - Newer .REG files, created by Windows XP and above use unicode UCS-2
+ * - @="" constructions should write value with empty name.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+
+/**
+ * @file
+ * @brief Registry patch files
+ */
+
+#define HEADER_STRING "REGEDIT4"
+
+struct dotreg_data {
+ int fd;
+};
+
+/*
+ * This is basically a copy of data_blob_hex_string_upper, but with comma's
+ * between the bytes in hex.
+ */
+static char *dotreg_data_blob_hex_string(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob)
+{
+ size_t i;
+ char *hex_string;
+
+ hex_string = talloc_array(mem_ctx, char, (blob->length*3)+1);
+ if (!hex_string) {
+ return NULL;
+ }
+
+ for (i = 0; i < blob->length; i++)
+ slprintf(&hex_string[i*3], 4, "%02X,", blob->data[i]);
+
+ /* Remove last comma and NULL-terminate the string */
+ hex_string[(blob->length*3)-1] = '\0';
+ return hex_string;
+}
+
+/*
+ * This is basically a copy of reg_val_data_string, except that this function
+ * has no 0x for dwords, everything else is regarded as binary, and binary
+ * strings are represented with bytes comma-separated.
+ */
+static char *reg_val_dotreg_string(TALLOC_CTX *mem_ctx, uint32_t type,
+ const DATA_BLOB data)
+{
+ size_t converted_size = 0;
+ char *ret = NULL;
+
+ if (data.length == 0)
+ return talloc_strdup(mem_ctx, "");
+
+ switch (type) {
+ case REG_EXPAND_SZ:
+ case REG_SZ:
+ convert_string_talloc(mem_ctx,
+ CH_UTF16, CH_UNIX, data.data, data.length,
+ (void **)&ret, &converted_size);
+ break;
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN:
+ SMB_ASSERT(data.length == sizeof(uint32_t));
+ ret = talloc_asprintf(mem_ctx, "%08x",
+ IVAL(data.data, 0));
+ break;
+ default: /* default means treat as binary */
+ case REG_BINARY:
+ ret = dotreg_data_blob_hex_string(mem_ctx, &data);
+ break;
+ }
+
+ return ret;
+}
+
+static WERROR reg_dotreg_diff_add_key(void *_data, const char *key_name)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ fdprintf(data->fd, "\n[%s]\n", key_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_del_key(void *_data, const char *key_name)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ fdprintf(data->fd, "\n[-%s]\n", key_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_set_value(void *_data, const char *path,
+ const char *value_name,
+ uint32_t value_type, DATA_BLOB value)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+ char *data_string = reg_val_dotreg_string(NULL,
+ value_type, value);
+ char *data_incl_type;
+
+ W_ERROR_HAVE_NO_MEMORY(data_string);
+
+ switch (value_type) {
+ case REG_SZ:
+ data_incl_type = talloc_asprintf(data_string, "\"%s\"",
+ data_string);
+ break;
+ case REG_DWORD:
+ data_incl_type = talloc_asprintf(data_string,
+ "dword:%s", data_string);
+ break;
+ case REG_BINARY:
+ data_incl_type = talloc_asprintf(data_string, "hex:%s",
+ data_string);
+ break;
+ default:
+ data_incl_type = talloc_asprintf(data_string, "hex(%x):%s",
+ value_type, data_string);
+ break;
+ }
+
+ if (value_name[0] == '\0') {
+ fdprintf(data->fd, "@=%s\n", data_incl_type);
+ } else {
+ fdprintf(data->fd, "\"%s\"=%s\n",
+ value_name, data_incl_type);
+ }
+
+ talloc_free(data_string);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_del_value(void *_data, const char *path,
+ const char *value_name)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ fdprintf(data->fd, "\"%s\"=-\n", value_name);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_done(void *_data)
+{
+ struct dotreg_data *data = (struct dotreg_data *)_data;
+
+ close(data->fd);
+ talloc_free(data);
+
+ return WERR_OK;
+}
+
+static WERROR reg_dotreg_diff_del_all_values(void *callback_data,
+ const char *key_name)
+{
+ return WERR_NOT_SUPPORTED;
+}
+
+/**
+ * Save registry diff
+ */
+_PUBLIC_ WERROR reg_dotreg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data)
+{
+ struct dotreg_data *data;
+
+ data = talloc_zero(ctx, struct dotreg_data);
+ *callback_data = data;
+
+ if (filename) {
+ data->fd = open(filename, O_CREAT|O_WRONLY, 0755);
+ if (data->fd < 0) {
+ DEBUG(0, ("Unable to open %s\n", filename));
+ return WERR_FILE_NOT_FOUND;
+ }
+ } else {
+ data->fd = STDOUT_FILENO;
+ }
+
+ fdprintf(data->fd, "%s\n\n", HEADER_STRING);
+
+ *callbacks = talloc(ctx, struct reg_diff_callbacks);
+
+ (*callbacks)->add_key = reg_dotreg_diff_add_key;
+ (*callbacks)->del_key = reg_dotreg_diff_del_key;
+ (*callbacks)->set_value = reg_dotreg_diff_set_value;
+ (*callbacks)->del_value = reg_dotreg_diff_del_value;
+ (*callbacks)->del_all_values = reg_dotreg_diff_del_all_values;
+ (*callbacks)->done = reg_dotreg_diff_done;
+
+ return WERR_OK;
+}
+
+/**
+ * Load diff file
+ */
+_PUBLIC_ WERROR reg_dotreg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ char *line, *p, *q;
+ char *curkey = NULL;
+ TALLOC_CTX *mem_ctx = talloc_init("reg_dotreg_diff_load");
+ WERROR error;
+ uint32_t value_type;
+ DATA_BLOB data;
+ bool result;
+ char *type_str = NULL;
+ char *data_str = NULL;
+ char *value = NULL;
+ bool continue_next_line = 0;
+
+ line = afdgets(fd, mem_ctx, 0);
+ if (!line) {
+ DEBUG(0, ("Can't read from file.\n"));
+ talloc_free(mem_ctx);
+ close(fd);
+ return WERR_GEN_FAILURE;
+ }
+
+ while ((line = afdgets(fd, mem_ctx, 0))) {
+ /* Remove '\r' if it's a Windows text file */
+ if (strlen(line) && line[strlen(line)-1] == '\r') {
+ line[strlen(line)-1] = '\0';
+ }
+
+ /* Ignore comments and empty lines */
+ if (strlen(line) == 0 || line[0] == ';') {
+ talloc_free(line);
+
+ if (curkey) {
+ talloc_free(curkey);
+ }
+ curkey = NULL;
+ continue;
+ }
+
+ /* Start of key */
+ if (line[0] == '[') {
+ if (line[strlen(line)-1] != ']') {
+ DEBUG(0, ("Missing ']' on line: %s\n", line));
+ talloc_free(line);
+ continue;
+ }
+
+ /* Deleting key */
+ if (line[1] == '-') {
+ curkey = talloc_strndup(line, line+2, strlen(line)-3);
+ W_ERROR_HAVE_NO_MEMORY(curkey);
+
+ error = callbacks->del_key(callback_data,
+ curkey);
+
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0,("Error deleting key %s\n",
+ curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ talloc_free(line);
+ curkey = NULL;
+ continue;
+ }
+ curkey = talloc_strndup(mem_ctx, line+1, strlen(line)-2);
+ W_ERROR_HAVE_NO_MEMORY(curkey);
+
+ error = callbacks->add_key(callback_data, curkey);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0,("Error adding key %s\n", curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ talloc_free(line);
+ continue;
+ }
+
+ /* Deleting/Changing value */
+ if (continue_next_line) {
+ continue_next_line = 0;
+
+ /* Continued data start with two whitespaces */
+ if (line[0] != ' ' || line[1] != ' ') {
+ DEBUG(0, ("Malformed line: %s\n", line));
+ talloc_free(line);
+ continue;
+ }
+ p = line + 2;
+
+ /* Continue again if line ends with a backslash */
+ if (line[strlen(line)-1] == '\\') {
+ line[strlen(line)-1] = '\0';
+ continue_next_line = 1;
+ data_str = talloc_strdup_append(data_str, p);
+ talloc_free(line);
+ continue;
+ }
+ data_str = talloc_strdup_append(data_str, p);
+ } else {
+ p = strchr_m(line, '=');
+ if (p == NULL) {
+ DEBUG(0, ("Malformed line: %s\n", line));
+ talloc_free(line);
+ continue;
+ }
+
+ *p = '\0'; p++;
+
+
+ if (curkey == NULL) {
+ DEBUG(0, ("Value change without key\n"));
+ talloc_free(line);
+ continue;
+ }
+
+ /* Values should be double-quoted */
+ if (line[0] != '"') {
+ DEBUG(0, ("Malformed line\n"));
+ talloc_free(line);
+ continue;
+ }
+
+ /* Chop of the quotes and store as value */
+ value = talloc_strndup(mem_ctx, line+1,strlen(line)-2);
+
+ /* Delete value */
+ if (p[0] == '-') {
+ error = callbacks->del_value(callback_data,
+ curkey, value);
+
+ /* Ignore if key does not exist (WERR_FILE_NOT_FOUND)
+ * Consistent with Windows behaviour */
+ if (!W_ERROR_IS_OK(error) &&
+ !W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND)) {
+ DEBUG(0, ("Error deleting value %s in key %s\n",
+ value, curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ talloc_free(line);
+ talloc_free(value);
+ continue;
+ }
+
+ /* Do not look for colons in strings */
+ if (p[0] == '"') {
+ q = NULL;
+ data_str = talloc_strndup(mem_ctx, p+1,strlen(p)-2);
+ } else {
+ /* Split the value type from the data */
+ q = strchr_m(p, ':');
+ if (q) {
+ *q = '\0';
+ q++;
+ type_str = talloc_strdup(mem_ctx, p);
+ data_str = talloc_strdup(mem_ctx, q);
+ } else {
+ data_str = talloc_strdup(mem_ctx, p);
+ }
+ }
+
+ /* Backslash before the CRLF means continue on next line */
+ if (data_str[strlen(data_str)-1] == '\\') {
+ data_str[strlen(data_str)-1] = '\0';
+ talloc_free(line);
+ continue_next_line = 1;
+ continue;
+ }
+ }
+ DEBUG(9, ("About to write %s with type %s, length %ld: %s\n", value, type_str, (long) strlen(data_str), data_str));
+ result = reg_string_to_val(value,
+ type_str?type_str:"REG_SZ", data_str,
+ &value_type, &data);
+ if (!result) {
+ DEBUG(0, ("Error converting string to value for line:\n%s\n",
+ line));
+ return WERR_GEN_FAILURE;
+ }
+
+ error = callbacks->set_value(callback_data, curkey, value,
+ value_type, data);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error setting value for %s in %s\n",
+ value, curkey));
+ talloc_free(mem_ctx);
+ return error;
+ }
+
+ /* Clean up buffers */
+ if (type_str != NULL) {
+ talloc_free(type_str);
+ type_str = NULL;
+ }
+ talloc_free(data_str);
+ talloc_free(value);
+ talloc_free(line);
+ }
+
+ close(fd);
+
+ talloc_free(mem_ctx);
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/patchfile_preg.c b/source4/lib/registry/patchfile_preg.c
new file mode 100644
index 0000000..50bd141
--- /dev/null
+++ b/source4/lib/registry/patchfile_preg.c
@@ -0,0 +1,387 @@
+/*
+ Unix SMB/CIFS implementation.
+ Reading Registry.pol PReg registry files
+
+ Copyright (C) Wilco Baan Hofman 2006-2008
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "system/filesys.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "lib/util/sys_rw.h"
+
+#undef strcasecmp
+#undef strncasecmp
+
+struct preg_data {
+ int fd;
+ TALLOC_CTX *ctx;
+};
+
+static WERROR preg_read_utf16(int fd, char *c)
+{
+ uint16_t v;
+
+ if (read(fd, &v, sizeof(uint16_t)) < sizeof(uint16_t)) {
+ return WERR_GEN_FAILURE;
+ }
+ push_codepoint(c, v);
+ return WERR_OK;
+}
+static WERROR preg_write_utf16(int fd, const char *string)
+{
+ uint16_t v;
+ size_t i, size;
+
+ for (i = 0; i < strlen(string); i+=size) {
+ v = next_codepoint(&string[i], &size);
+ if (write(fd, &v, sizeof(uint16_t)) < sizeof(uint16_t)) {
+ return WERR_GEN_FAILURE;
+ }
+ }
+ return WERR_OK;
+}
+/* PReg does not support adding keys. */
+static WERROR reg_preg_diff_add_key(void *_data, const char *key_name)
+{
+ return WERR_OK;
+}
+
+static WERROR reg_preg_diff_set_value(void *_data, const char *key_name,
+ const char *value_name,
+ uint32_t value_type, DATA_BLOB value_data)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ uint32_t buf;
+
+ preg_write_utf16(data->fd, "[");
+ preg_write_utf16(data->fd, key_name);
+ preg_write_utf16(data->fd, ";");
+ preg_write_utf16(data->fd, value_name);
+ preg_write_utf16(data->fd, ";");
+ SIVAL(&buf, 0, value_type);
+ sys_write_v(data->fd, &buf, sizeof(uint32_t));
+ preg_write_utf16(data->fd, ";");
+ SIVAL(&buf, 0, value_data.length);
+ sys_write_v(data->fd, &buf, sizeof(uint32_t));
+ preg_write_utf16(data->fd, ";");
+ sys_write_v(data->fd, value_data.data, value_data.length);
+ preg_write_utf16(data->fd, "]");
+
+ return WERR_OK;
+}
+
+static WERROR reg_preg_diff_del_key(void *_data, const char *key_name)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ char *parent_name;
+ DATA_BLOB blob;
+ WERROR werr;
+
+ parent_name = talloc_strndup(data->ctx, key_name,
+ strrchr(key_name, '\\')-key_name);
+ W_ERROR_HAVE_NO_MEMORY(parent_name);
+ blob.data = (uint8_t*)talloc_strndup(data->ctx,
+ key_name+(strrchr(key_name, '\\')-key_name)+1,
+ strlen(key_name)-(strrchr(key_name, '\\')-key_name));
+ W_ERROR_HAVE_NO_MEMORY(blob.data);
+ blob.length = strlen((char *)blob.data)+1;
+
+
+ /* FIXME: These values should be accumulated to be written at done(). */
+ werr = reg_preg_diff_set_value(data, parent_name, "**DeleteKeys",
+ REG_SZ, blob);
+
+ talloc_free(parent_name);
+ talloc_free(blob.data);
+
+ return werr;
+}
+
+static WERROR reg_preg_diff_del_value(void *_data, const char *key_name,
+ const char *value_name)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ char *val;
+ DATA_BLOB blob;
+ WERROR werr;
+
+ val = talloc_asprintf(data->ctx, "**Del.%s", value_name);
+ W_ERROR_HAVE_NO_MEMORY(val);
+ blob.data = (uint8_t *)talloc(data->ctx, uint32_t);
+ W_ERROR_HAVE_NO_MEMORY(blob.data);
+ SIVAL(blob.data, 0, 0);
+ blob.length = sizeof(uint32_t);
+
+ werr = reg_preg_diff_set_value(data, key_name, val, REG_DWORD, blob);
+
+ talloc_free(val);
+ talloc_free(blob.data);
+
+ return werr;
+}
+
+static WERROR reg_preg_diff_del_all_values(void *_data, const char *key_name)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+ DATA_BLOB blob;
+ WERROR werr;
+
+ blob.data = (uint8_t *)talloc(data->ctx, uint32_t);
+ W_ERROR_HAVE_NO_MEMORY(blob.data);
+ SIVAL(blob.data, 0, 0);
+ blob.length = sizeof(uint32_t);
+
+ werr = reg_preg_diff_set_value(data, key_name, "**DelVals.", REG_DWORD,
+ blob);
+
+ talloc_free(blob.data);
+
+ return werr;
+}
+
+static WERROR reg_preg_diff_done(void *_data)
+{
+ struct preg_data *data = (struct preg_data *)_data;
+
+ close(data->fd);
+ talloc_free(data);
+ return WERR_OK;
+}
+
+/**
+ * Save registry diff
+ */
+_PUBLIC_ WERROR reg_preg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data)
+{
+ struct preg_data *data;
+ struct {
+ char hdr[4];
+ uint32_t version;
+ } preg_header;
+
+
+ data = talloc_zero(ctx, struct preg_data);
+ *callback_data = data;
+
+ if (filename) {
+ data->fd = open(filename, O_CREAT|O_WRONLY, 0755);
+ if (data->fd < 0) {
+ DEBUG(0, ("Unable to open %s\n", filename));
+ return WERR_FILE_NOT_FOUND;
+ }
+ } else {
+ data->fd = STDOUT_FILENO;
+ }
+
+ memcpy(preg_header.hdr, "PReg", sizeof(preg_header.hdr));
+ SIVAL(&preg_header.version, 0, 1);
+ sys_write_v(data->fd, (uint8_t *)&preg_header, sizeof(preg_header));
+
+ data->ctx = ctx;
+
+ *callbacks = talloc(ctx, struct reg_diff_callbacks);
+
+ (*callbacks)->add_key = reg_preg_diff_add_key;
+ (*callbacks)->del_key = reg_preg_diff_del_key;
+ (*callbacks)->set_value = reg_preg_diff_set_value;
+ (*callbacks)->del_value = reg_preg_diff_del_value;
+ (*callbacks)->del_all_values = reg_preg_diff_del_all_values;
+ (*callbacks)->done = reg_preg_diff_done;
+
+ return WERR_OK;
+}
+/**
+ * Load diff file
+ */
+_PUBLIC_ WERROR reg_preg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data)
+{
+ struct {
+ char hdr[4];
+ uint32_t version;
+ } preg_header;
+ char *buf;
+ size_t buf_size = 1024;
+ char *buf_ptr;
+ TALLOC_CTX *mem_ctx = talloc_init("reg_preg_diff_load");
+ WERROR ret = WERR_OK;
+
+ buf = talloc_array(mem_ctx, char, buf_size);
+ buf_ptr = buf;
+
+ /* Read first 8 bytes (the header) */
+ if (read(fd, &preg_header, sizeof(preg_header)) != sizeof(preg_header)) {
+ DEBUG(0, ("Could not read PReg file: %s\n",
+ strerror(errno)));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+ preg_header.version = IVAL(&preg_header.version, 0);
+
+ if (strncmp(preg_header.hdr, "PReg", 4) != 0) {
+ DEBUG(0, ("This file is not a valid preg registry file\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+ if (preg_header.version > 1) {
+ DEBUG(0, ("Warning: file format version is higher than expected.\n"));
+ }
+
+ /* Read the entries */
+ while(1) {
+ uint32_t value_type, length;
+ char *key = NULL;
+ char *value_name = NULL;
+ DATA_BLOB data = {NULL, 0};
+
+ if (!W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr))) {
+ break;
+ }
+ if (*buf_ptr != '[') {
+ DEBUG(0, ("Error in PReg file.\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+
+ /* Get the path */
+ buf_ptr = buf;
+ while (W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
+ *buf_ptr != ';' && buf_ptr-buf < buf_size) {
+ buf_ptr++;
+ }
+ buf[buf_ptr-buf] = '\0';
+ key = talloc_strdup(mem_ctx, buf);
+
+ /* Get the name */
+ buf_ptr = buf;
+ while (W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
+ *buf_ptr != ';' && buf_ptr-buf < buf_size) {
+ buf_ptr++;
+ }
+ buf[buf_ptr-buf] = '\0';
+ value_name = talloc_strdup(mem_ctx, buf);
+
+ /* Get the type */
+ if (read(fd, &value_type, sizeof(uint32_t)) < sizeof(uint32_t)) {
+ DEBUG(0, ("Error while reading PReg\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+ value_type = IVAL(&value_type, 0);
+
+ /* Read past delimiter */
+ buf_ptr = buf;
+ if (!(W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
+ *buf_ptr == ';') && buf_ptr-buf < buf_size) {
+ DEBUG(0, ("Error in PReg file.\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+
+ /* Get data length */
+ if (read(fd, &length, sizeof(uint32_t)) < sizeof(uint32_t)) {
+ DEBUG(0, ("Error while reading PReg\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+ length = IVAL(&length, 0);
+
+ /* Read past delimiter */
+ buf_ptr = buf;
+ if (!(W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
+ *buf_ptr == ';') && buf_ptr-buf < buf_size) {
+ DEBUG(0, ("Error in PReg file.\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+
+ /* Get the data */
+ buf_ptr = buf;
+ if (length < buf_size &&
+ read(fd, buf_ptr, length) != length) {
+ DEBUG(0, ("Error while reading PReg\n"));
+ ret = WERR_GEN_FAILURE;
+ goto cleanup;
+ }
+ data = data_blob_talloc(mem_ctx, buf, length);
+
+ /* Check if delimiter is in place (whine if it isn't) */
+ buf_ptr = buf;
+ if (!(W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) &&
+ *buf_ptr == ']') && buf_ptr-buf < buf_size) {
+ DEBUG(0, ("Warning: Missing ']' in PReg file, expected ']', got '%c' 0x%x.\n",
+ *buf_ptr, *buf_ptr));
+ }
+
+ if (strcasecmp(value_name, "**DelVals") == 0) {
+ callbacks->del_all_values(callback_data, key);
+ } else if (strncasecmp(value_name, "**Del.",6) == 0) {
+ char *p = value_name+6;
+
+ callbacks->del_value(callback_data, key, p);
+ } else if (strcasecmp(value_name, "**DeleteValues") == 0) {
+ char *p, *q;
+
+ p = (char *) data.data;
+
+ while ((q = strchr_m(p, ';'))) {
+ *q = '\0';
+ q++;
+
+ callbacks->del_value(callback_data, key, p);
+
+ p = q;
+ }
+ callbacks->del_value(callback_data, key, p);
+ } else if (strcasecmp(value_name, "**DeleteKeys") == 0) {
+ char *p, *q, *full_key;
+
+ p = (char *) data.data;
+
+ while ((q = strchr_m(p, ';'))) {
+ *q = '\0';
+ q++;
+
+ full_key = talloc_asprintf(mem_ctx, "%s\\%s",
+ key, p);
+ callbacks->del_key(callback_data, full_key);
+ talloc_free(full_key);
+
+ p = q;
+ }
+ full_key = talloc_asprintf(mem_ctx, "%s\\%s", key, p);
+ callbacks->del_key(callback_data, full_key);
+ talloc_free(full_key);
+ } else {
+ callbacks->add_key(callback_data, key);
+ callbacks->set_value(callback_data, key, value_name,
+ value_type, data);
+ }
+ TALLOC_FREE(key);
+ TALLOC_FREE(value_name);
+ data_blob_free(&data);
+ }
+cleanup:
+ close(fd);
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
diff --git a/source4/lib/registry/pyregistry.c b/source4/lib/registry/pyregistry.c
new file mode 100644
index 0000000..f4201c3
--- /dev/null
+++ b/source4/lib/registry/pyregistry.c
@@ -0,0 +1,494 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba utility functions
+ Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
+ Copyright (C) Wilco Baan Hofman <wilco@baanhofman.nl> 2010
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+#include "python/py3compat.h"
+#include "includes.h"
+#include "python/modules.h"
+#include "libcli/util/pyerrors.h"
+#include "lib/registry/registry.h"
+#include <pytalloc.h>
+#include "lib/events/events.h"
+#include "auth/credentials/pycredentials.h"
+#include "param/pyparam.h"
+
+extern PyTypeObject PyRegistryKey;
+extern PyTypeObject PyRegistry;
+extern PyTypeObject PyHiveKey;
+
+/*#define PyRegistryKey_AsRegistryKey(obj) pytalloc_get_type(obj, struct registry_key)*/
+#define PyRegistry_AsRegistryContext(obj) ((struct registry_context *)pytalloc_get_ptr(obj))
+#define PyHiveKey_AsHiveKey(obj) ((struct hive_key*)pytalloc_get_ptr(obj))
+
+
+static PyObject *py_get_predefined_key_by_name(PyObject *self, PyObject *args)
+{
+ char *name;
+ WERROR result;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ struct registry_key *key;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ result = reg_get_predefined_key_by_name(ctx, name, &key);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ return pytalloc_steal(&PyRegistryKey, key);
+}
+
+static PyObject *py_key_del_abs(PyObject *self, PyObject *args)
+{
+ char *path;
+ WERROR result;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return NULL;
+
+ result = reg_key_del_abs(ctx, path);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_get_predefined_key(PyObject *self, PyObject *args)
+{
+ uint32_t hkey;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ WERROR result;
+ struct registry_key *key;
+
+ if (!PyArg_ParseTuple(args, "I", &hkey))
+ return NULL;
+
+ result = reg_get_predefined_key(ctx, hkey, &key);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ return pytalloc_steal(&PyRegistryKey, key);
+}
+
+static PyObject *py_diff_apply(PyObject *self, PyObject *args)
+{
+ char *filename;
+ WERROR result;
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ if (!PyArg_ParseTuple(args, "s", &filename))
+ return NULL;
+
+ result = reg_diff_apply(ctx, filename);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_mount_hive(PyObject *self, PyObject *args)
+{
+ struct registry_context *ctx = PyRegistry_AsRegistryContext(self);
+ uint32_t hkey;
+ PyObject *py_hivekey, *py_elements = Py_None;
+ const char **elements;
+ WERROR result;
+
+ if (!PyArg_ParseTuple(args, "OI|O", &py_hivekey, &hkey, &py_elements))
+ return NULL;
+
+ if (!PyList_Check(py_elements) && py_elements != Py_None) {
+ PyErr_SetString(PyExc_TypeError, "Expected list of elements");
+ return NULL;
+ }
+
+ if (py_elements == Py_None) {
+ elements = NULL;
+ } else {
+ int i;
+ elements = talloc_array(NULL, const char *, PyList_Size(py_elements));
+ for (i = 0; i < PyList_Size(py_elements); i++)
+ elements[i] = PyUnicode_AsUTF8(PyList_GetItem(py_elements, i));
+ }
+
+ SMB_ASSERT(ctx != NULL);
+
+ result = reg_mount_hive(ctx, PyHiveKey_AsHiveKey(py_hivekey), hkey, elements);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *registry_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ WERROR result;
+ struct registry_context *ctx;
+ result = reg_open_local(NULL, &ctx);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+ return pytalloc_steal(&PyRegistry, ctx);
+}
+
+static PyMethodDef registry_methods[] = {
+ { "get_predefined_key_by_name", py_get_predefined_key_by_name, METH_VARARGS,
+ "S.get_predefined_key_by_name(name) -> key\n"
+ "Find a predefined key by name" },
+ { "key_del_abs", py_key_del_abs, METH_VARARGS, "S.key_del_abs(name) -> None\n"
+ "Delete a key by absolute path." },
+ { "get_predefined_key", py_get_predefined_key, METH_VARARGS, "S.get_predefined_key(hkey_id) -> key\n"
+ "Find a predefined key by id" },
+ { "diff_apply", py_diff_apply, METH_VARARGS, "S.diff_apply(filename) -> None\n"
+ "Apply the diff from the specified file" },
+ { "mount_hive", py_mount_hive, METH_VARARGS, "S.mount_hive(key, key_id, elements=None) -> None\n"
+ "Mount the specified key at the specified path." },
+ {0}
+};
+
+PyTypeObject PyRegistry = {
+ .tp_name = "Registry",
+ .tp_methods = registry_methods,
+ .tp_new = registry_new,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+static PyObject *py_hive_key_del(PyObject *self, PyObject *args)
+{
+ char *name;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+ WERROR result;
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ result = hive_key_del(NULL, key, name);
+
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_hive_key_flush(PyObject *self,
+ PyObject *Py_UNUSED(ignored))
+{
+ WERROR result;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+
+ result = hive_key_flush(key);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_hive_key_del_value(PyObject *self, PyObject *args)
+{
+ char *name;
+ WERROR result;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+
+ if (!PyArg_ParseTuple(args, "s", &name))
+ return NULL;
+
+ result = hive_key_del_value(NULL, key, name);
+
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_hive_key_set_value(PyObject *self, PyObject *args)
+{
+ char *name;
+ uint32_t type;
+ DATA_BLOB value;
+ Py_ssize_t value_length = 0;
+ WERROR result;
+ struct hive_key *key = PyHiveKey_AsHiveKey(self);
+
+ if (!PyArg_ParseTuple(args, "sIz#", &name, &type, &value.data, &value_length)) {
+ return NULL;
+ }
+ value.length = value_length;
+
+ if (value.data != NULL)
+ result = hive_key_set_value(key, name, type, value);
+ else
+ result = hive_key_del_value(NULL, key, name);
+
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef hive_key_methods[] = {
+ { "del", py_hive_key_del, METH_VARARGS, "S.del(name) -> None\n"
+ "Delete a subkey" },
+ { "flush", (PyCFunction)py_hive_key_flush, METH_NOARGS, "S.flush() -> None\n"
+ "Flush this key to disk" },
+ { "del_value", py_hive_key_del_value, METH_VARARGS, "S.del_value(name) -> None\n"
+ "Delete a value" },
+ { "set_value", py_hive_key_set_value, METH_VARARGS, "S.set_value(name, type, data) -> None\n"
+ "Set a value" },
+ {0}
+};
+
+static PyObject *hive_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) {
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_open_hive(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "location", "lp_ctx", "session_info", "credentials", NULL };
+ WERROR result;
+ struct loadparm_context *lp_ctx;
+ PyObject *py_lp_ctx = Py_None;
+ PyObject *py_session_info = Py_None;
+ PyObject *py_credentials = Py_None;
+ struct auth_session_info *session_info;
+ struct cli_credentials *credentials;
+ char *location;
+ struct hive_key *hive_key;
+ TALLOC_CTX *mem_ctx;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOO",
+ discard_const_p(char *, kwnames),
+ &location,
+ &py_lp_ctx, &py_session_info,
+ &py_credentials))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ credentials = cli_credentials_from_py_object(py_credentials);
+ if (credentials == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ session_info = NULL;
+
+ result = reg_open_hive(NULL, location, session_info, credentials,
+ samba_tevent_context_init(NULL),
+ lp_ctx, &hive_key);
+ talloc_free(mem_ctx);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ return pytalloc_steal(&PyHiveKey, hive_key);
+}
+
+PyTypeObject PyHiveKey = {
+ .tp_name = "HiveKey",
+ .tp_methods = hive_key_methods,
+ .tp_new = hive_new,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+PyTypeObject PyRegistryKey = {
+ .tp_name = "RegistryKey",
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+};
+
+static PyObject *py_open_samba(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "lp_ctx", "session_info", NULL };
+ struct registry_context *reg_ctx;
+ WERROR result;
+ struct loadparm_context *lp_ctx;
+ PyObject *py_lp_ctx = Py_None;
+ PyObject *py_session_info = Py_None;
+ PyObject *py_credentials = Py_None;
+ struct auth_session_info *session_info;
+ struct cli_credentials *credentials;
+ TALLOC_CTX *mem_ctx;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOO",
+ discard_const_p(char *, kwnames),
+ &py_lp_ctx, &py_session_info,
+ &py_credentials))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ credentials = cli_credentials_from_py_object(py_credentials);
+ if (credentials == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ session_info = NULL; /* FIXME */
+
+ result = reg_open_samba(NULL, &reg_ctx, NULL,
+ lp_ctx, session_info, credentials);
+ talloc_free(mem_ctx);
+ if (!W_ERROR_IS_OK(result)) {
+ PyErr_SetWERROR(result);
+ return NULL;
+ }
+
+ return pytalloc_steal(&PyRegistry, reg_ctx);
+}
+
+static PyObject *py_open_ldb_file(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ const char *kwnames[] = { "location", "session_info", "credentials", "lp_ctx", NULL };
+ PyObject *py_session_info = Py_None, *py_credentials = Py_None, *py_lp_ctx = Py_None;
+ WERROR result;
+ char *location;
+ struct loadparm_context *lp_ctx;
+ struct cli_credentials *credentials;
+ struct hive_key *key;
+ struct auth_session_info *session_info;
+ TALLOC_CTX *mem_ctx;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|OOO",
+ discard_const_p(char *, kwnames),
+ &location, &py_session_info,
+ &py_credentials, &py_lp_ctx))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ lp_ctx = lpcfg_from_py_object(mem_ctx, py_lp_ctx);
+ if (lp_ctx == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected loadparm context");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ credentials = cli_credentials_from_py_object(py_credentials);
+ if (credentials == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials");
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ session_info = NULL; /* FIXME */
+
+ result = reg_open_ldb_file(NULL, location, session_info, credentials,
+ s4_event_context_init(NULL), lp_ctx, &key);
+ talloc_free(mem_ctx);
+ PyErr_WERROR_NOT_OK_RAISE(result);
+
+ return pytalloc_steal(&PyHiveKey, key);
+}
+
+static PyObject *py_str_regtype(PyObject *self, PyObject *args)
+{
+ int regtype;
+
+ if (!PyArg_ParseTuple(args, "i", &regtype))
+ return NULL;
+
+ return PyUnicode_FromString(str_regtype(regtype));
+}
+
+static PyObject *py_get_predef_name(PyObject *self, PyObject *args)
+{
+ uint32_t hkey;
+ const char *str;
+
+ if (!PyArg_ParseTuple(args, "I", &hkey))
+ return NULL;
+
+ str = reg_get_predef_name(hkey);
+ if (str == NULL)
+ Py_RETURN_NONE;
+ return PyUnicode_FromString(str);
+}
+
+static PyMethodDef py_registry_methods[] = {
+ { "open_samba", PY_DISCARD_FUNC_SIG(PyCFunction, py_open_samba),
+ METH_VARARGS|METH_KEYWORDS, "open_samba() -> reg" },
+ { "open_ldb", PY_DISCARD_FUNC_SIG(PyCFunction, py_open_ldb_file),
+ METH_VARARGS|METH_KEYWORDS, "open_ldb(location, session_info=None, credentials=None, loadparm_context=None) -> key" },
+ { "open_hive", PY_DISCARD_FUNC_SIG(PyCFunction, py_open_hive),
+ METH_VARARGS|METH_KEYWORDS, "open_hive(location, session_info=None, credentials=None, loadparm_context=None) -> key" },
+ { "str_regtype", py_str_regtype, METH_VARARGS, "str_regtype(int) -> str" },
+ { "get_predef_name", py_get_predef_name, METH_VARARGS, "get_predef_name(hkey) -> str" },
+ {0}
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "registry",
+ .m_doc = "Registry",
+ .m_size = -1,
+ .m_methods = py_registry_methods,
+};
+
+MODULE_INIT_FUNC(registry)
+{
+ PyObject *m;
+
+ if (pytalloc_BaseObject_PyType_Ready(&PyHiveKey) < 0)
+ return NULL;
+
+ if (pytalloc_BaseObject_PyType_Ready(&PyRegistry) < 0)
+ return NULL;
+
+ if (pytalloc_BaseObject_PyType_Ready(&PyRegistryKey) < 0)
+ return NULL;
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL)
+ return NULL;
+
+ PyModule_AddObject(m, "HKEY_CLASSES_ROOT", PyLong_FromLong(HKEY_CLASSES_ROOT));
+ PyModule_AddObject(m, "HKEY_CURRENT_USER", PyLong_FromLong(HKEY_CURRENT_USER));
+ PyModule_AddObject(m, "HKEY_LOCAL_MACHINE", PyLong_FromLong(HKEY_LOCAL_MACHINE));
+ PyModule_AddObject(m, "HKEY_USERS", PyLong_FromLong(HKEY_USERS));
+ PyModule_AddObject(m, "HKEY_PERFORMANCE_DATA", PyLong_FromLong(HKEY_PERFORMANCE_DATA));
+ PyModule_AddObject(m, "HKEY_CURRENT_CONFIG", PyLong_FromLong(HKEY_CURRENT_CONFIG));
+ PyModule_AddObject(m, "HKEY_DYN_DATA", PyLong_FromLong(HKEY_DYN_DATA));
+ PyModule_AddObject(m, "HKEY_PERFORMANCE_TEXT", PyLong_FromLong(HKEY_PERFORMANCE_TEXT));
+ PyModule_AddObject(m, "HKEY_PERFORMANCE_NLSTEXT", PyLong_FromLong(HKEY_PERFORMANCE_NLSTEXT));
+
+ Py_INCREF(&PyRegistry);
+ PyModule_AddObject(m, "Registry", (PyObject *)&PyRegistry);
+
+ Py_INCREF(&PyHiveKey);
+ PyModule_AddObject(m, "HiveKey", (PyObject *)&PyHiveKey);
+
+ Py_INCREF(&PyRegistryKey);
+ PyModule_AddObject(m, "RegistryKey", (PyObject *)&PyRegistryKey);
+
+ return m;
+}
diff --git a/source4/lib/registry/regf.c b/source4/lib/registry/regf.c
new file mode 100644
index 0000000..1e0b143
--- /dev/null
+++ b/source4/lib/registry/regf.c
@@ -0,0 +1,2319 @@
+/*
+ Samba CIFS implementation
+ Registry backend for REGF files
+ Copyright (C) 2005-2007 Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2006-2010 Wilco Baan Hofman, wilco@baanhofman.nl
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "lib/registry/tdr_regf.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "lib/registry/registry.h"
+#include "libcli/security/security.h"
+
+#undef strcasecmp
+
+static struct hive_operations reg_backend_regf;
+
+/**
+ * There are several places on the web where the REGF format is explained;
+ *
+ * TODO: Links
+ */
+
+/* TODO:
+ * - Return error codes that make more sense
+ * - Locking
+ * - do more things in-memory
+ */
+
+/*
+ * Read HBIN blocks into memory
+ */
+
+struct regf_data {
+ int fd;
+ struct hbin_block **hbins;
+ struct regf_hdr *header;
+ time_t last_write;
+};
+
+static WERROR regf_save_hbin(struct regf_data *data, bool flush);
+
+struct regf_key_data {
+ struct hive_key key;
+ struct regf_data *hive;
+ uint32_t offset;
+ struct nk_block *nk;
+};
+
+static struct hbin_block *hbin_by_offset(const struct regf_data *data,
+ uint32_t offset, uint32_t *rel_offset)
+{
+ unsigned int i;
+
+ for (i = 0; data->hbins[i]; i++) {
+ if (offset >= data->hbins[i]->offset_from_first &&
+ offset < data->hbins[i]->offset_from_first+
+ data->hbins[i]->offset_to_next) {
+ if (rel_offset != NULL)
+ *rel_offset = offset - data->hbins[i]->offset_from_first - 0x20;
+ return data->hbins[i];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * Validate a regf header
+ * For now, do nothing, but we should check the checksum
+ */
+static uint32_t regf_hdr_checksum(const uint8_t *buffer)
+{
+ uint32_t checksum = 0, x;
+ unsigned int i;
+
+ for (i = 0; i < 0x01FB; i+= 4) {
+ x = IVAL(buffer, i);
+ checksum ^= x;
+ }
+
+ return checksum;
+}
+
+/**
+ * Obtain the contents of a HBIN block
+ */
+static DATA_BLOB hbin_get(const struct regf_data *data, uint32_t offset)
+{
+ DATA_BLOB ret;
+ struct hbin_block *hbin;
+ uint32_t rel_offset;
+
+ ret.data = NULL;
+ ret.length = 0;
+
+ hbin = hbin_by_offset(data, offset, &rel_offset);
+
+ if (hbin == NULL) {
+ DEBUG(1, ("Can't find HBIN at 0x%04x\n", offset));
+ return ret;
+ }
+
+ ret.length = IVAL(hbin->data, rel_offset);
+ if (!(ret.length & 0x80000000)) {
+ DEBUG(0, ("Trying to use dirty block at 0x%04x\n", offset));
+ return ret;
+ }
+
+ /* remove high bit */
+ ret.length = (ret.length ^ 0xffffffff) + 1;
+
+ ret.length -= 4; /* 4 bytes for the length... */
+ ret.data = hbin->data +
+ (offset - hbin->offset_from_first - 0x20) + 4;
+
+ return ret;
+}
+
+static bool hbin_get_tdr(struct regf_data *regf, uint32_t offset,
+ TALLOC_CTX *ctx, tdr_pull_fn_t pull_fn, void *p)
+{
+ struct tdr_pull *pull = tdr_pull_init(regf);
+
+ pull->data = hbin_get(regf, offset);
+ if (!pull->data.data) {
+ DEBUG(1, ("Unable to get data at 0x%04x\n", offset));
+ talloc_free(pull);
+ return false;
+ }
+
+ if (NT_STATUS_IS_ERR(pull_fn(pull, ctx, p))) {
+ DEBUG(1, ("Error parsing record at 0x%04x using tdr\n",
+ offset));
+ talloc_free(pull);
+ return false;
+ }
+ talloc_free(pull);
+
+ return true;
+}
+
+/* Allocate some new data */
+static DATA_BLOB hbin_alloc(struct regf_data *data, uint32_t size,
+ uint32_t *offset)
+{
+ DATA_BLOB ret;
+ uint32_t rel_offset = (uint32_t) -1; /* Relative offset ! */
+ struct hbin_block *hbin = NULL;
+ unsigned int i;
+
+ if (offset != NULL) {
+ *offset = 0;
+ }
+
+ if (size == 0)
+ return data_blob(NULL, 0);
+
+ size += 4; /* Need to include int32 for the length */
+
+ /* Allocate as a multiple of 8 */
+ size = (size + 7) & ~7;
+
+ ret.data = NULL;
+ ret.length = 0;
+
+ for (i = 0; (hbin = data->hbins[i]); i++) {
+ int j;
+ int32_t my_size;
+ for (j = 0; j < hbin->offset_to_next-0x20; j+= my_size) {
+ my_size = IVALS(hbin->data, j);
+
+ if (my_size == 0x0) {
+ DEBUG(0, ("Invalid zero-length block! File is corrupt.\n"));
+ return ret;
+ }
+
+ if (my_size % 8 != 0) {
+ DEBUG(0, ("Encountered non-aligned block!\n"));
+ }
+
+ if (my_size < 0) { /* Used... */
+ my_size = -my_size;
+ } else if (my_size == size) { /* exact match */
+ rel_offset = j;
+ DEBUG(4, ("Found free block of exact size %d in middle of HBIN\n",
+ size));
+ break;
+ } else if (my_size > size) { /* data will remain */
+ rel_offset = j;
+ /* Split this block and mark the next block as free */
+ SIVAL(hbin->data, rel_offset+size, my_size-size);
+ DEBUG(4, ("Found free block of size %d (needing %d) in middle of HBIN\n",
+ my_size, size));
+ break;
+ }
+ }
+
+ if (rel_offset != -1)
+ break;
+ }
+
+ /* No space available in previous hbins,
+ * allocate new one */
+ if (data->hbins[i] == NULL) {
+ DEBUG(4, ("No space available in other HBINs for block of size %d, allocating new HBIN\n",
+ size));
+
+ /* Add extra hbin block */
+ data->hbins = talloc_realloc(data, data->hbins,
+ struct hbin_block *, i+2);
+ hbin = talloc(data->hbins, struct hbin_block);
+ SMB_ASSERT(hbin != NULL);
+
+ data->hbins[i] = hbin;
+ data->hbins[i+1] = NULL;
+
+ /* Set hbin data */
+ hbin->HBIN_ID = talloc_strdup(hbin, "hbin");
+ hbin->offset_from_first = (i == 0?0:data->hbins[i-1]->offset_from_first+data->hbins[i-1]->offset_to_next);
+ hbin->offset_to_next = 0x1000;
+ hbin->unknown[0] = 0;
+ hbin->unknown[1] = 0;
+ unix_to_nt_time(&hbin->last_change, time(NULL));
+ hbin->block_size = hbin->offset_to_next;
+ hbin->data = talloc_zero_array(hbin, uint8_t, hbin->block_size - 0x20);
+ /* Update the regf header */
+ data->header->last_block += hbin->offset_to_next;
+
+ /* Set the next block to it's proper size and set the
+ * rel_offset for this block */
+ SIVAL(hbin->data, size, hbin->block_size - size - 0x20);
+ rel_offset = 0x0;
+ }
+
+ /* Set size and mark as used */
+ SIVAL(hbin->data, rel_offset, -size);
+
+ ret.data = hbin->data + rel_offset + 0x4; /* Skip past length */
+ ret.length = size - 0x4;
+ if (offset) {
+ uint32_t new_rel_offset = 0;
+ *offset = hbin->offset_from_first + rel_offset + 0x20;
+ SMB_ASSERT(hbin_by_offset(data, *offset, &new_rel_offset) == hbin);
+ SMB_ASSERT(new_rel_offset == rel_offset);
+ }
+
+ return ret;
+}
+
+/* Store a data blob. Return the offset at which it was stored */
+static uint32_t hbin_store (struct regf_data *data, DATA_BLOB blob)
+{
+ uint32_t ret;
+ DATA_BLOB dest = hbin_alloc(data, blob.length, &ret);
+
+ memcpy(dest.data, blob.data, blob.length);
+
+ /* Make sure that we have no tailing garbage in the block */
+ if (dest.length > blob.length) {
+ memset(dest.data + blob.length, 0, dest.length - blob.length);
+ }
+
+ return ret;
+}
+
+static uint32_t hbin_store_tdr(struct regf_data *data,
+ tdr_push_fn_t push_fn, void *p)
+{
+ struct tdr_push *push = tdr_push_init(data);
+ uint32_t ret;
+
+ if (NT_STATUS_IS_ERR(push_fn(push, p))) {
+ DEBUG(0, ("Error during push\n"));
+ return -1;
+ }
+
+ ret = hbin_store(data, push->data);
+
+ talloc_free(push);
+
+ return ret;
+}
+
+
+/* Free existing data */
+static void hbin_free (struct regf_data *data, uint32_t offset)
+{
+ int32_t size;
+ uint32_t rel_offset;
+ int32_t next_size;
+ struct hbin_block *hbin;
+
+ SMB_ASSERT (offset > 0);
+
+ hbin = hbin_by_offset(data, offset, &rel_offset);
+
+ if (hbin == NULL)
+ return;
+
+ /* Get original size */
+ size = IVALS(hbin->data, rel_offset);
+
+ if (size > 0) {
+ DEBUG(1, ("Trying to free already freed block at 0x%04x\n",
+ offset));
+ return;
+ }
+ /* Mark as unused */
+ size = -size;
+
+ /* If the next block is free, merge into big free block */
+ if (rel_offset + size < hbin->offset_to_next - 0x20) {
+ next_size = IVALS(hbin->data, rel_offset+size);
+ if (next_size > 0) {
+ size += next_size;
+ }
+ }
+
+ /* Write block size */
+ SIVALS(hbin->data, rel_offset, size);
+}
+
+/**
+ * Store a data blob data was already stored, but has changed in size
+ * Will try to save it at the current location if possible, otherwise
+ * does a free + store */
+static uint32_t hbin_store_resize(struct regf_data *data,
+ uint32_t orig_offset, DATA_BLOB blob)
+{
+ uint32_t rel_offset;
+ struct hbin_block *hbin = hbin_by_offset(data, orig_offset,
+ &rel_offset);
+ int32_t my_size;
+ int32_t orig_size;
+ int32_t needed_size;
+ int32_t possible_size;
+ unsigned int i;
+
+ SMB_ASSERT(orig_offset > 0);
+
+ if (!hbin)
+ return hbin_store(data, blob);
+
+ /* Get original size */
+ orig_size = -IVALS(hbin->data, rel_offset);
+
+ needed_size = blob.length + 4; /* Add int32 containing length */
+ needed_size = (needed_size + 7) & ~7; /* Align */
+
+ /* Fits into current allocated block */
+ if (orig_size >= needed_size) {
+ memcpy(hbin->data + rel_offset + 0x4, blob.data, blob.length);
+ /* If the difference in size is greater than 0x4, split the block
+ * and free/merge it */
+ if (orig_size - needed_size > 0x4) {
+ SIVALS(hbin->data, rel_offset, -needed_size);
+ SIVALS(hbin->data, rel_offset + needed_size,
+ needed_size-orig_size);
+ hbin_free(data, orig_offset + needed_size);
+ }
+ return orig_offset;
+ }
+
+ possible_size = orig_size;
+
+ /* Check if it can be combined with the next few free records */
+ for (i = rel_offset; i < hbin->offset_to_next - 0x20; i += my_size) {
+ if (IVALS(hbin->data, i) < 0) /* Used */
+ break;
+
+ my_size = IVALS(hbin->data, i);
+
+ if (my_size == 0x0) {
+ DEBUG(0, ("Invalid zero-length block! File is corrupt.\n"));
+ break;
+ } else {
+ possible_size += my_size;
+ }
+
+ if (possible_size >= blob.length) {
+ SIVAL(hbin->data, rel_offset, -possible_size);
+ memcpy(hbin->data + rel_offset + 0x4,
+ blob.data, blob.length);
+ return orig_offset;
+ }
+ }
+
+ hbin_free(data, orig_offset);
+ return hbin_store(data, blob);
+}
+
+static uint32_t hbin_store_tdr_resize(struct regf_data *regf,
+ tdr_push_fn_t push_fn,
+ uint32_t orig_offset, void *p)
+{
+ struct tdr_push *push = tdr_push_init(regf);
+ uint32_t ret;
+
+ if (NT_STATUS_IS_ERR(push_fn(push, p))) {
+ DEBUG(0, ("Error during push\n"));
+ return -1;
+ }
+
+ ret = hbin_store_resize(regf, orig_offset, push->data);
+
+ talloc_free(push);
+
+ return ret;
+}
+
+static uint32_t regf_create_lh_hash(const char *name)
+{
+ char *hash_name;
+ uint32_t ret = 0;
+ uint16_t i;
+
+ hash_name = strupper_talloc(NULL, name);
+ for (i = 0; *(hash_name + i) != 0; i++) {
+ ret *= 37;
+ ret += *(hash_name + i);
+ }
+ talloc_free(hash_name);
+ return ret;
+}
+
+static WERROR regf_get_info(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_mod_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+
+ if (num_subkeys != NULL)
+ *num_subkeys = private_data->nk->num_subkeys;
+
+ if (num_values != NULL)
+ *num_values = private_data->nk->num_values;
+
+ if (classname != NULL) {
+ if (private_data->nk->clsname_offset != -1) {
+ DATA_BLOB data = hbin_get(private_data->hive,
+ private_data->nk->clsname_offset);
+ *classname = talloc_strndup(mem_ctx,
+ (char*)data.data,
+ private_data->nk->clsname_length);
+ W_ERROR_HAVE_NO_MEMORY(*classname);
+ } else
+ *classname = NULL;
+ }
+
+ /* TODO: Last mod time */
+
+ /* TODO: max valnamelen */
+
+ /* TODO: max valbufsize */
+
+ /* TODO: max subkeynamelen */
+
+ return WERR_OK;
+}
+
+static struct regf_key_data *regf_get_key(TALLOC_CTX *ctx,
+ struct regf_data *regf,
+ uint32_t offset)
+{
+ struct nk_block *nk;
+ struct regf_key_data *ret;
+
+ ret = talloc_zero(ctx, struct regf_key_data);
+ ret->key.ops = &reg_backend_regf;
+ ret->hive = talloc_reference(ret, regf);
+ ret->offset = offset;
+ nk = talloc(ret, struct nk_block);
+ if (nk == NULL)
+ return NULL;
+
+ ret->nk = nk;
+
+ if (!hbin_get_tdr(regf, offset, nk,
+ (tdr_pull_fn_t)tdr_pull_nk_block, nk)) {
+ DEBUG(0, ("Unable to find HBIN data for offset 0x%x\n", offset));
+ return NULL;
+ }
+
+ if (strcmp(nk->header, "nk") != 0) {
+ DEBUG(0, ("Expected nk record, got %s\n", nk->header));
+ talloc_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+
+static WERROR regf_get_value(TALLOC_CTX *ctx, struct hive_key *key,
+ uint32_t idx, const char **name,
+ uint32_t *data_type, DATA_BLOB *data)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct vk_block *vk;
+ struct regf_data *regf = private_data->hive;
+ uint32_t vk_offset;
+ DATA_BLOB tmp;
+
+ if (idx >= private_data->nk->num_values)
+ return WERR_NO_MORE_ITEMS;
+
+ tmp = hbin_get(regf, private_data->nk->values_offset);
+ if (!tmp.data) {
+ DEBUG(0, ("Unable to find value list at 0x%x\n",
+ private_data->nk->values_offset));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (tmp.length < private_data->nk->num_values * 4) {
+ DEBUG(1, ("Value counts mismatch\n"));
+ }
+
+ vk_offset = IVAL(tmp.data, idx * 4);
+
+ vk = talloc(NULL, struct vk_block);
+ W_ERROR_HAVE_NO_MEMORY(vk);
+
+ if (!hbin_get_tdr(regf, vk_offset, vk,
+ (tdr_pull_fn_t)tdr_pull_vk_block, vk)) {
+ DEBUG(0, ("Unable to get VK block at 0x%x\n", vk_offset));
+ talloc_free(vk);
+ return WERR_GEN_FAILURE;
+ }
+
+ /* FIXME: name character set ?*/
+ if (name != NULL) {
+ *name = talloc_strndup(ctx, vk->data_name, vk->name_length);
+ W_ERROR_HAVE_NO_MEMORY(*name);
+ }
+
+ if (data_type != NULL)
+ *data_type = vk->data_type;
+
+ if (vk->data_length & 0x80000000) {
+ /* this is data of type "REG_DWORD" or "REG_DWORD_BIG_ENDIAN" */
+ data->data = talloc_size(ctx, sizeof(uint32_t));
+ W_ERROR_HAVE_NO_MEMORY(data->data);
+ SIVAL(data->data, 0, vk->data_offset);
+ data->length = sizeof(uint32_t);
+ } else {
+ *data = hbin_get(regf, vk->data_offset);
+ }
+
+ if (data->length < vk->data_length) {
+ DEBUG(1, ("Read data less than indicated data length!\n"));
+ }
+
+ talloc_free(vk);
+
+ return WERR_OK;
+}
+
+static WERROR regf_get_value_by_name(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data)
+{
+ unsigned int i;
+ const char *vname;
+ WERROR error;
+
+ /* FIXME: Do binary search? Is this list sorted at all? */
+
+ for (i = 0; W_ERROR_IS_OK(error = regf_get_value(mem_ctx, key, i,
+ &vname, type, data));
+ i++) {
+ if (!strcmp(vname, name))
+ return WERR_OK;
+ }
+
+ if (W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS))
+ return WERR_FILE_NOT_FOUND;
+
+ return error;
+}
+
+
+static WERROR regf_get_subkey_by_index(TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ uint32_t idx, const char **name,
+ const char **classname,
+ NTTIME *last_mod_time)
+{
+ DATA_BLOB data;
+ struct regf_key_data *ret;
+ const struct regf_key_data *private_data = (const struct regf_key_data *)key;
+ struct nk_block *nk = private_data->nk;
+ uint32_t key_off=0;
+
+ if (idx >= nk->num_subkeys)
+ return WERR_NO_MORE_ITEMS;
+
+ /* Make sure that we don't crash if the key is empty */
+ if (nk->subkeys_offset == -1) {
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ data = hbin_get(private_data->hive, nk->subkeys_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list at 0x%x\n",
+ nk->subkeys_offset));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (!strncmp((char *)data.data, "li", 2)) {
+ struct li_block li;
+ struct tdr_pull *pull = tdr_pull_init(private_data->hive);
+
+ DEBUG(10, ("Subkeys in LI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, nk, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ if (li.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+ key_off = li.nk_offset[idx];
+
+ } else if (!strncmp((char *)data.data, "lf", 2)) {
+ struct lf_block lf;
+ struct tdr_pull *pull = tdr_pull_init(private_data->hive);
+
+ DEBUG(10, ("Subkeys in LF list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, nk, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ if (lf.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ key_off = lf.hr[idx].nk_offset;
+ } else if (!strncmp((char *)data.data, "lh", 2)) {
+ struct lh_block lh;
+ struct tdr_pull *pull = tdr_pull_init(private_data->hive);
+
+ DEBUG(10, ("Subkeys in LH list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, nk, &lh))) {
+ DEBUG(0, ("Error parsing LH list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ if (lh.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+ key_off = lh.hr[idx].nk_offset;
+ } else if (!strncmp((char *)data.data, "ri", 2)) {
+ struct ri_block ri;
+ struct tdr_pull *pull = tdr_pull_init(ctx);
+ uint16_t i;
+ uint16_t sublist_count = 0;
+
+ DEBUG(10, ("Subkeys in RI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_ri_block(pull, nk, &ri))) {
+ DEBUG(0, ("Error parsing RI list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(ri.header, "ri", 2));
+
+ for (i = 0; i < ri.key_count; i++) {
+ DATA_BLOB list_data;
+
+ /* Get sublist data blob */
+ list_data = hbin_get(private_data->hive, ri.offset[i]);
+ if (!list_data.data) {
+ DEBUG(0, ("Error getting RI list."));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+
+ pull->data = list_data;
+
+ if (!strncmp((char *)list_data.data, "li", 2)) {
+ struct li_block li;
+
+ DEBUG(10, ("Subkeys in RI->LI list\n"));
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull,
+ nk,
+ &li))) {
+ DEBUG(0, ("Error parsing LI list from RI\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ /* Advance to next sublist if necessary */
+ if (idx >= sublist_count + li.key_count) {
+ sublist_count += li.key_count;
+ continue;
+ }
+ key_off = li.nk_offset[idx - sublist_count];
+ sublist_count += li.key_count;
+ break;
+ } else if (!strncmp((char *)list_data.data, "lh", 2)) {
+ struct lh_block lh;
+
+ DEBUG(10, ("Subkeys in RI->LH list\n"));
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull,
+ nk,
+ &lh))) {
+ DEBUG(0, ("Error parsing LH list from RI\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ /* Advance to next sublist if necessary */
+ if (idx >= sublist_count + lh.key_count) {
+ sublist_count += lh.key_count;
+ continue;
+ }
+ key_off = lh.hr[idx - sublist_count].nk_offset;
+ sublist_count += lh.key_count;
+ break;
+ } else {
+ DEBUG(0,("Unknown sublist in ri block\n"));
+ talloc_free(pull);
+
+ return WERR_GEN_FAILURE;
+ }
+
+ }
+ talloc_free(pull);
+
+
+ if (idx > sublist_count) {
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ } else {
+ DEBUG(0, ("Unknown type for subkey list (0x%04x): %c%c\n",
+ nk->subkeys_offset, data.data[0], data.data[1]));
+ return WERR_GEN_FAILURE;
+ }
+
+ ret = regf_get_key (ctx, private_data->hive, key_off);
+
+ if (classname != NULL) {
+ if (ret->nk->clsname_offset != -1) {
+ DATA_BLOB db = hbin_get(ret->hive,
+ ret->nk->clsname_offset);
+ *classname = talloc_strndup(ctx,
+ (char*)db.data,
+ ret->nk->clsname_length);
+ W_ERROR_HAVE_NO_MEMORY(*classname);
+ } else
+ *classname = NULL;
+ }
+
+ if (last_mod_time != NULL)
+ *last_mod_time = ret->nk->last_change;
+
+ if (name != NULL)
+ *name = talloc_steal(ctx, ret->nk->key_name);
+
+ talloc_free(ret);
+
+ return WERR_OK;
+}
+
+static WERROR regf_match_subkey_by_name(TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ uint32_t offset,
+ const char *name, uint32_t *ret)
+{
+ DATA_BLOB subkey_data;
+ struct nk_block subkey;
+ struct tdr_pull *pull;
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+
+ subkey_data = hbin_get(private_data->hive, offset);
+ if (!subkey_data.data) {
+ DEBUG(0, ("Unable to retrieve subkey HBIN\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ pull = tdr_pull_init(ctx);
+
+ pull->data = subkey_data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_nk_block(pull, ctx, &subkey))) {
+ DEBUG(0, ("Error parsing NK structure.\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+
+ if (strncmp(subkey.header, "nk", 2)) {
+ DEBUG(0, ("Not an NK structure.\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (!strcasecmp(subkey.key_name, name)) {
+ *ret = offset;
+ } else {
+ *ret = 0;
+ }
+ return WERR_OK;
+}
+
+static WERROR regf_get_subkey_by_name(TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ const char *name,
+ struct hive_key **ret)
+{
+ DATA_BLOB data;
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct nk_block *nk = private_data->nk;
+ uint32_t key_off = 0;
+
+ /* Make sure that we don't crash if the key is empty */
+ if (nk->subkeys_offset == -1) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ data = hbin_get(private_data->hive, nk->subkeys_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (!strncmp((char *)data.data, "li", 2)) {
+ struct li_block li;
+ struct tdr_pull *pull = tdr_pull_init(ctx);
+ uint16_t i;
+
+ DEBUG(10, ("Subkeys in LI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, nk, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ if (li.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ for (i = 0; i < li.key_count; i++) {
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key,
+ li.nk_offset[i],
+ name,
+ &key_off));
+ if (key_off != 0)
+ break;
+ }
+ if (key_off == 0)
+ return WERR_FILE_NOT_FOUND;
+ } else if (!strncmp((char *)data.data, "lf", 2)) {
+ struct lf_block lf;
+ struct tdr_pull *pull = tdr_pull_init(ctx);
+ uint16_t i;
+
+ DEBUG(10, ("Subkeys in LF list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, nk, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ if (lf.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ for (i = 0; i < lf.key_count; i++) {
+ if (strncmp(lf.hr[i].hash, name, 4)) {
+ continue;
+ }
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk,
+ key,
+ lf.hr[i].nk_offset,
+ name,
+ &key_off));
+ if (key_off != 0)
+ break;
+ }
+ if (key_off == 0)
+ return WERR_FILE_NOT_FOUND;
+ } else if (!strncmp((char *)data.data, "lh", 2)) {
+ struct lh_block lh;
+ struct tdr_pull *pull = tdr_pull_init(ctx);
+ uint16_t i;
+ uint32_t hash;
+
+ DEBUG(10, ("Subkeys in LH list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, nk, &lh))) {
+ DEBUG(0, ("Error parsing LH list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ if (lh.key_count != nk->num_subkeys) {
+ DEBUG(0, ("Subkey counts don't match\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ hash = regf_create_lh_hash(name);
+ for (i = 0; i < lh.key_count; i++) {
+ if (lh.hr[i].base37 != hash) {
+ continue;
+ }
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk,
+ key,
+ lh.hr[i].nk_offset,
+ name,
+ &key_off));
+ if (key_off != 0)
+ break;
+ }
+ if (key_off == 0)
+ return WERR_FILE_NOT_FOUND;
+ } else if (!strncmp((char *)data.data, "ri", 2)) {
+ struct ri_block ri;
+ struct tdr_pull *pull = tdr_pull_init(ctx);
+ uint16_t i, j;
+
+ DEBUG(10, ("Subkeys in RI list\n"));
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_ri_block(pull, nk, &ri))) {
+ DEBUG(0, ("Error parsing RI list\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(ri.header, "ri", 2));
+
+ for (i = 0; i < ri.key_count; i++) {
+ DATA_BLOB list_data;
+
+ /* Get sublist data blob */
+ list_data = hbin_get(private_data->hive, ri.offset[i]);
+ if (list_data.data == NULL) {
+ DEBUG(0, ("Error getting RI list."));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+
+ pull->data = list_data;
+
+ if (!strncmp((char *)list_data.data, "li", 2)) {
+ struct li_block li;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull,
+ nk,
+ &li))) {
+ DEBUG(0, ("Error parsing LI list from RI\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ for (j = 0; j < li.key_count; j++) {
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key,
+ li.nk_offset[j],
+ name,
+ &key_off));
+ if (key_off)
+ break;
+ }
+ } else if (!strncmp((char *)list_data.data, "lh", 2)) {
+ struct lh_block lh;
+ uint32_t hash;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull,
+ nk,
+ &lh))) {
+ DEBUG(0, ("Error parsing LH list from RI\n"));
+ talloc_free(pull);
+ return WERR_GEN_FAILURE;
+ }
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ hash = regf_create_lh_hash(name);
+ for (j = 0; j < lh.key_count; j++) {
+ if (lh.hr[j].base37 != hash) {
+ continue;
+ }
+ W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key,
+ lh.hr[j].nk_offset,
+ name,
+ &key_off));
+ if (key_off)
+ break;
+ }
+ }
+ if (key_off)
+ break;
+ }
+ talloc_free(pull);
+ if (!key_off)
+ return WERR_FILE_NOT_FOUND;
+ } else {
+ DEBUG(0, ("Unknown subkey list type.\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ *ret = (struct hive_key *)regf_get_key(ctx, private_data->hive,
+ key_off);
+ return WERR_OK;
+}
+
+static WERROR regf_set_sec_desc(struct hive_key *key,
+ const struct security_descriptor *sec_desc)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct sk_block cur_sk, sk, new_sk;
+ struct regf_data *regf = private_data->hive;
+ struct nk_block root;
+ DATA_BLOB data;
+ uint32_t sk_offset, cur_sk_offset;
+ bool update_cur_sk = false;
+
+ /* Get the root nk */
+ hbin_get_tdr(regf, regf->header->data_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_nk_block, &root);
+
+ /* Push the security descriptor to a blob */
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_struct_blob(&data, regf,
+ sec_desc, (ndr_push_flags_fn_t)ndr_push_security_descriptor))) {
+ DEBUG(0, ("Unable to push security descriptor\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ /* Get the current security descriptor for the key */
+ if (!hbin_get_tdr(regf, private_data->nk->sk_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &cur_sk)) {
+ DEBUG(0, ("Unable to find security descriptor for current key\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ /* If there's no change, change nothing. */
+ if (memcmp(data.data, cur_sk.sec_desc,
+ MIN(data.length, cur_sk.rec_size)) == 0) {
+ return WERR_OK;
+ }
+
+ /* Delete the current sk if only this key is using it */
+ if (cur_sk.ref_cnt == 1) {
+ /* Get the previous security descriptor for the key */
+ if (!hbin_get_tdr(regf, cur_sk.prev_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find prev security descriptor for current key\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ /* Change and store the previous security descriptor */
+ sk.next_offset = cur_sk.next_offset;
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_sk_block,
+ cur_sk.prev_offset, &sk);
+
+ /* Get the next security descriptor for the key */
+ if (!hbin_get_tdr(regf, cur_sk.next_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find next security descriptor for current key\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ /* Change and store the next security descriptor */
+ sk.prev_offset = cur_sk.prev_offset;
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_sk_block,
+ cur_sk.next_offset, &sk);
+
+ hbin_free(regf, private_data->nk->sk_offset);
+ } else {
+ /* This key will no longer be referring to this sk */
+ cur_sk.ref_cnt--;
+ update_cur_sk = true;
+ }
+
+ sk_offset = root.sk_offset;
+
+ do {
+ cur_sk_offset = sk_offset;
+ if (!hbin_get_tdr(regf, sk_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ if (memcmp(data.data, sk.sec_desc, MIN(data.length, sk.rec_size)) == 0) {
+ private_data->nk->sk_offset = sk_offset;
+ sk.ref_cnt++;
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ sk_offset, &sk);
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset,
+ private_data->nk);
+ return WERR_OK;
+ }
+ sk_offset = sk.next_offset;
+ } while (sk_offset != root.sk_offset);
+
+ ZERO_STRUCT(new_sk);
+ new_sk.header = "sk";
+ new_sk.prev_offset = cur_sk_offset;
+ new_sk.next_offset = root.sk_offset;
+ new_sk.ref_cnt = 1;
+ new_sk.rec_size = data.length;
+ new_sk.sec_desc = data.data;
+
+ sk_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ &new_sk);
+ if (sk_offset == -1) {
+ DEBUG(0, ("Error storing sk block\n"));
+ return WERR_GEN_FAILURE;
+ }
+ private_data->nk->sk_offset = sk_offset;
+
+ if (update_cur_sk) {
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ private_data->nk->sk_offset, &cur_sk);
+ }
+
+ /* Get the previous security descriptor for the key */
+ if (!hbin_get_tdr(regf, new_sk.prev_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor for previous key\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ /* Change and store the previous security descriptor */
+ sk.next_offset = sk_offset;
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ cur_sk.prev_offset, &sk);
+
+ /* Get the next security descriptor for the key (always root, as we append) */
+ if (!hbin_get_tdr(regf, new_sk.next_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor for current key\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ /* Change and store the next security descriptor (always root, as we append) */
+ sk.prev_offset = sk_offset;
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ root.sk_offset, &sk);
+
+
+ /* Store the nk. */
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ private_data->offset, private_data->nk);
+ return WERR_OK;
+}
+
+static WERROR regf_get_sec_desc(TALLOC_CTX *ctx, const struct hive_key *key,
+ struct security_descriptor **sd)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)key;
+ struct sk_block sk;
+ struct regf_data *regf = private_data->hive;
+ DATA_BLOB data;
+
+ if (!hbin_get_tdr(regf, private_data->nk->sk_offset, ctx,
+ (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
+ DEBUG(0, ("Unable to find security descriptor\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (strcmp(sk.header, "sk") != 0) {
+ DEBUG(0, ("Expected 'sk', got '%s'\n", sk.header));
+ return WERR_GEN_FAILURE;
+ }
+
+ *sd = talloc(ctx, struct security_descriptor);
+ W_ERROR_HAVE_NO_MEMORY(*sd);
+
+ data.data = sk.sec_desc;
+ data.length = sk.rec_size;
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_pull_struct_blob(&data, ctx, *sd,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor))) {
+ DEBUG(0, ("Error parsing security descriptor\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR regf_sl_add_entry(struct regf_data *regf, uint32_t list_offset,
+ const char *name,
+ uint32_t key_offset, uint32_t *ret)
+{
+ DATA_BLOB data;
+
+ /* Create a new key if necessary */
+ if (list_offset == -1) {
+ if (regf->header->version.major != 1) {
+ DEBUG(0, ("Can't store keys in unknown registry format\n"));
+ return WERR_NOT_SUPPORTED;
+ }
+ if (regf->header->version.minor < 3) {
+ /* Store LI */
+ struct li_block li;
+ ZERO_STRUCT(li);
+ li.header = "li";
+ li.key_count = 1;
+
+ li.nk_offset = talloc_array(regf, uint32_t, 1);
+ W_ERROR_HAVE_NO_MEMORY(li.nk_offset);
+ li.nk_offset[0] = key_offset;
+
+ *ret = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_li_block,
+ &li);
+
+ talloc_free(li.nk_offset);
+ } else if (regf->header->version.minor == 3 ||
+ regf->header->version.minor == 4) {
+ /* Store LF */
+ struct lf_block lf;
+ ZERO_STRUCT(lf);
+ lf.header = "lf";
+ lf.key_count = 1;
+
+ lf.hr = talloc_array(regf, struct hash_record, 1);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr);
+ lf.hr[0].nk_offset = key_offset;
+ lf.hr[0].hash = talloc_strndup(lf.hr, name, 4);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr[0].hash);
+
+ *ret = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_lf_block,
+ &lf);
+
+ talloc_free(lf.hr);
+ } else if (regf->header->version.minor == 5) {
+ /* Store LH */
+ struct lh_block lh;
+ ZERO_STRUCT(lh);
+ lh.header = "lh";
+ lh.key_count = 1;
+
+ lh.hr = talloc_array(regf, struct lh_hash, 1);
+ W_ERROR_HAVE_NO_MEMORY(lh.hr);
+ lh.hr[0].nk_offset = key_offset;
+ lh.hr[0].base37 = regf_create_lh_hash(name);
+
+ *ret = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_lh_block,
+ &lh);
+
+ talloc_free(lh.hr);
+ }
+ return WERR_OK;
+ }
+
+ data = hbin_get(regf, list_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if (!strncmp((char *)data.data, "li", 2)) {
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ struct li_block li;
+ struct nk_block sub_nk;
+ int32_t i, j;
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, regf, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+
+ if (strncmp(li.header, "li", 2) != 0) {
+ abort();
+ DEBUG(0, ("LI header corrupt\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ /*
+ * Find the position to store the pointer
+ * Extensive testing reveils that at least on windows 7 subkeys
+ * *MUST* be stored in alphabetical order
+ */
+ for (i = 0; i < li.key_count; i++) {
+ /* Get the nk */
+ hbin_get_tdr(regf, li.nk_offset[i], regf,
+ (tdr_pull_fn_t) tdr_pull_nk_block, &sub_nk);
+ if (strcasecmp(name, sub_nk.key_name) < 0) {
+ break;
+ }
+ }
+
+ li.nk_offset = talloc_realloc(regf, li.nk_offset,
+ uint32_t, li.key_count+1);
+ W_ERROR_HAVE_NO_MEMORY(li.nk_offset);
+
+ /* Move everything behind this offset */
+ for (j = li.key_count - 1; j >= i; j--) {
+ li.nk_offset[j+1] = li.nk_offset[j];
+ }
+
+ li.nk_offset[i] = key_offset;
+ li.key_count++;
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t)tdr_push_li_block,
+ list_offset, &li);
+
+ talloc_free(li.nk_offset);
+ } else if (!strncmp((char *)data.data, "lf", 2)) {
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ struct lf_block lf;
+ struct nk_block sub_nk;
+ int32_t i, j;
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, regf, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ /*
+ * Find the position to store the hash record
+ * Extensive testing reveils that at least on windows 7 subkeys
+ * *MUST* be stored in alphabetical order
+ */
+ for (i = 0; i < lf.key_count; i++) {
+ /* Get the nk */
+ hbin_get_tdr(regf, lf.hr[i].nk_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_nk_block, &sub_nk);
+ if (strcasecmp(name, sub_nk.key_name) < 0) {
+ break;
+ }
+ }
+
+ lf.hr = talloc_realloc(regf, lf.hr, struct hash_record,
+ lf.key_count+1);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr);
+
+ /* Move everything behind this hash record */
+ for (j = lf.key_count - 1; j >= i; j--) {
+ lf.hr[j+1] = lf.hr[j];
+ }
+
+ lf.hr[i].nk_offset = key_offset;
+ lf.hr[i].hash = talloc_strndup(lf.hr, name, 4);
+ W_ERROR_HAVE_NO_MEMORY(lf.hr[lf.key_count].hash);
+ lf.key_count++;
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t)tdr_push_lf_block,
+ list_offset, &lf);
+
+ talloc_free(lf.hr);
+ } else if (!strncmp((char *)data.data, "lh", 2)) {
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ struct lh_block lh;
+ struct nk_block sub_nk;
+ int32_t i, j;
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, regf, &lh))) {
+ DEBUG(0, ("Error parsing LH list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ /*
+ * Find the position to store the hash record
+ * Extensive testing reveils that at least on windows 7 subkeys
+ * *MUST* be stored in alphabetical order
+ */
+ for (i = 0; i < lh.key_count; i++) {
+ /* Get the nk */
+ hbin_get_tdr(regf, lh.hr[i].nk_offset, regf,
+ (tdr_pull_fn_t) tdr_pull_nk_block, &sub_nk);
+ if (strcasecmp(name, sub_nk.key_name) < 0) {
+ break;
+ }
+ }
+
+ lh.hr = talloc_realloc(regf, lh.hr, struct lh_hash,
+ lh.key_count+1);
+ W_ERROR_HAVE_NO_MEMORY(lh.hr);
+
+ /* Move everything behind this hash record */
+ for (j = lh.key_count - 1; j >= i; j--) {
+ lh.hr[j+1] = lh.hr[j];
+ }
+
+ lh.hr[i].nk_offset = key_offset;
+ lh.hr[i].base37 = regf_create_lh_hash(name);
+ lh.key_count++;
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t)tdr_push_lh_block,
+ list_offset, &lh);
+
+ talloc_free(lh.hr);
+ } else if (!strncmp((char *)data.data, "ri", 2)) {
+ /* FIXME */
+ DEBUG(0, ("Adding to 'ri' subkey list is not supported yet.\n"));
+ return WERR_NOT_SUPPORTED;
+ } else {
+ DEBUG(0, ("Cannot add to unknown subkey list\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR regf_sl_del_entry(struct regf_data *regf, uint32_t list_offset,
+ uint32_t key_offset, uint32_t *ret)
+{
+ DATA_BLOB data;
+
+ data = hbin_get(regf, list_offset);
+ if (!data.data) {
+ DEBUG(0, ("Unable to find subkey list\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if (strncmp((char *)data.data, "li", 2) == 0) {
+ struct li_block li;
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ uint16_t i;
+ bool found_offset = false;
+
+ DEBUG(10, ("Subkeys in LI list\n"));
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_li_block(pull, regf, &li))) {
+ DEBUG(0, ("Error parsing LI list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+
+ SMB_ASSERT(!strncmp(li.header, "li", 2));
+
+ for (i = 0; i < li.key_count; i++) {
+ if (found_offset) {
+ li.nk_offset[i-1] = li.nk_offset[i];
+ }
+ if (li.nk_offset[i] == key_offset) {
+ found_offset = true;
+ continue;
+ }
+ }
+ if (!found_offset) {
+ DEBUG(2, ("Subkey not found\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ li.key_count--;
+
+ /* If the there are no entries left, free the subkey list */
+ if (li.key_count == 0) {
+ hbin_free(regf, list_offset);
+ *ret = -1;
+ }
+
+ /* Store li block */
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_li_block,
+ list_offset, &li);
+ } else if (strncmp((char *)data.data, "lf", 2) == 0) {
+ struct lf_block lf;
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ uint16_t i;
+ bool found_offset = false;
+
+ DEBUG(10, ("Subkeys in LF list\n"));
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, regf, &lf))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+
+ SMB_ASSERT(!strncmp(lf.header, "lf", 2));
+
+ for (i = 0; i < lf.key_count; i++) {
+ if (found_offset) {
+ lf.hr[i-1] = lf.hr[i];
+ continue;
+ }
+ if (lf.hr[i].nk_offset == key_offset) {
+ found_offset = 1;
+ continue;
+ }
+ }
+ if (!found_offset) {
+ DEBUG(2, ("Subkey not found\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ lf.key_count--;
+
+ /* If the there are no entries left, free the subkey list */
+ if (lf.key_count == 0) {
+ hbin_free(regf, list_offset);
+ *ret = -1;
+ return WERR_OK;
+ }
+
+ /* Store lf block */
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_lf_block,
+ list_offset, &lf);
+ } else if (strncmp((char *)data.data, "lh", 2) == 0) {
+ struct lh_block lh;
+ struct tdr_pull *pull = tdr_pull_init(regf);
+ uint16_t i;
+ bool found_offset = false;
+
+ DEBUG(10, ("Subkeys in LH list\n"));
+
+ pull->data = data;
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lh_block(pull, regf, &lh))) {
+ DEBUG(0, ("Error parsing LF list\n"));
+ talloc_free(pull);
+ return WERR_FILE_NOT_FOUND;
+ }
+ talloc_free(pull);
+
+ SMB_ASSERT(!strncmp(lh.header, "lh", 2));
+
+ for (i = 0; i < lh.key_count; i++) {
+ if (found_offset) {
+ lh.hr[i-1] = lh.hr[i];
+ continue;
+ }
+ if (lh.hr[i].nk_offset == key_offset) {
+ found_offset = 1;
+ continue;
+ }
+ }
+ if (!found_offset) {
+ DEBUG(0, ("Subkey not found\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ lh.key_count--;
+
+ /* If the there are no entries left, free the subkey list */
+ if (lh.key_count == 0) {
+ hbin_free(regf, list_offset);
+ *ret = -1;
+ return WERR_OK;
+ }
+
+ /* Store lh block */
+ *ret = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_lh_block,
+ list_offset, &lh);
+ } else if (strncmp((char *)data.data, "ri", 2) == 0) {
+ /* FIXME */
+ DEBUG(0, ("Sorry, deletion from ri block is not supported yet.\n"));
+ return WERR_NOT_SUPPORTED;
+ } else {
+ DEBUG (0, ("Unknown header found in subkey list.\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+ return WERR_OK;
+}
+
+static WERROR regf_del_value(TALLOC_CTX *mem_ctx, struct hive_key *key,
+ const char *name)
+{
+ struct regf_key_data *private_data = (struct regf_key_data *)key;
+ struct regf_data *regf = private_data->hive;
+ struct nk_block *nk = private_data->nk;
+ struct vk_block vk;
+ uint32_t vk_offset;
+ bool found_offset = false;
+ DATA_BLOB values;
+ unsigned int i;
+
+ if (nk->values_offset == -1) {
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ values = hbin_get(regf, nk->values_offset);
+
+ for (i = 0; i < nk->num_values; i++) {
+ if (found_offset) {
+ ((uint32_t *)values.data)[i-1] = ((uint32_t *) values.data)[i];
+ } else {
+ vk_offset = IVAL(values.data, i * 4);
+ if (!hbin_get_tdr(regf, vk_offset, private_data,
+ (tdr_pull_fn_t)tdr_pull_vk_block,
+ &vk)) {
+ DEBUG(0, ("Unable to get VK block at %d\n",
+ vk_offset));
+ return WERR_FILE_NOT_FOUND;
+ }
+ if (strcmp(vk.data_name, name) == 0) {
+ hbin_free(regf, vk_offset);
+ found_offset = true;
+ }
+ }
+ }
+ if (!found_offset) {
+ return WERR_FILE_NOT_FOUND;
+ } else {
+ nk->num_values--;
+ values.length = (nk->num_values)*4;
+ }
+
+ /* Store values list and nk */
+ if (nk->num_values == 0) {
+ hbin_free(regf, nk->values_offset);
+ nk->values_offset = -1;
+ } else {
+ nk->values_offset = hbin_store_resize(regf,
+ nk->values_offset,
+ values);
+ }
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset, nk);
+
+ return regf_save_hbin(private_data->hive, 0);
+}
+
+
+static WERROR regf_del_key(TALLOC_CTX *mem_ctx, const struct hive_key *parent,
+ const char *name)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)parent;
+ struct regf_key_data *key;
+ struct nk_block *parent_nk;
+ WERROR error;
+
+ SMB_ASSERT(private_data);
+
+ parent_nk = private_data->nk;
+
+ if (parent_nk->subkeys_offset == -1) {
+ DEBUG(4, ("Subkey list is empty, this key cannot contain subkeys.\n"));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ /* Find the key */
+ if (!W_ERROR_IS_OK(regf_get_subkey_by_name(parent_nk, parent, name,
+ (struct hive_key **)&key))) {
+ DEBUG(2, ("Key '%s' not found\n", name));
+ return WERR_FILE_NOT_FOUND;
+ }
+
+ if (key->nk->subkeys_offset != -1) {
+ struct hive_key *sk = (struct hive_key *)key;
+ unsigned int i = key->nk->num_subkeys;
+ while (i--) {
+ char *sk_name;
+ const char *p = NULL;
+
+ /* Get subkey information. */
+ error = regf_get_subkey_by_index(parent_nk, sk, 0,
+ &p,
+ NULL, NULL);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't retrieve subkey by index.\n"));
+ return error;
+ }
+ sk_name = discard_const_p(char, p);
+
+ /* Delete subkey. */
+ error = regf_del_key(NULL, sk, sk_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't delete key '%s'.\n", sk_name));
+ return error;
+ }
+
+ talloc_free(sk_name);
+ }
+ }
+
+ if (key->nk->values_offset != -1) {
+ struct hive_key *sk = (struct hive_key *)key;
+ DATA_BLOB data;
+ unsigned int i = key->nk->num_values;
+ while (i--) {
+ char *val_name;
+ const char *p = NULL;
+
+ /* Get value information. */
+ error = regf_get_value(parent_nk, sk, 0,
+ &p,
+ NULL, &data);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't retrieve value by index.\n"));
+ return error;
+ }
+ val_name = discard_const_p(char, p);
+
+ /* Delete value. */
+ error = regf_del_value(NULL, sk, val_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't delete value '%s'.\n", val_name));
+ return error;
+ }
+
+ talloc_free(val_name);
+ }
+ }
+
+ /* Delete it from the subkey list. */
+ error = regf_sl_del_entry(private_data->hive, parent_nk->subkeys_offset,
+ key->offset, &parent_nk->subkeys_offset);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Can't store new subkey list for parent key. Won't delete.\n"));
+ return error;
+ }
+
+ /* Re-store parent key */
+ parent_nk->num_subkeys--;
+ hbin_store_tdr_resize(private_data->hive,
+ (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset, parent_nk);
+
+ if (key->nk->clsname_offset != -1) {
+ hbin_free(private_data->hive, key->nk->clsname_offset);
+ }
+ hbin_free(private_data->hive, key->offset);
+
+ return regf_save_hbin(private_data->hive, 0);
+}
+
+static WERROR regf_add_key(TALLOC_CTX *ctx, const struct hive_key *parent,
+ const char *name, const char *classname,
+ struct security_descriptor *sec_desc,
+ struct hive_key **ret)
+{
+ const struct regf_key_data *private_data =
+ (const struct regf_key_data *)parent;
+ struct nk_block *parent_nk = private_data->nk, nk;
+ struct nk_block *root;
+ struct regf_data *regf = private_data->hive;
+ uint32_t offset;
+ WERROR error;
+
+ nk.header = "nk";
+ nk.type = REG_SUB_KEY;
+ unix_to_nt_time(&nk.last_change, time(NULL));
+ nk.uk1 = 0;
+ nk.parent_offset = private_data->offset;
+ nk.num_subkeys = 0;
+ nk.uk2 = 0;
+ nk.subkeys_offset = -1;
+ nk.unknown_offset = -1;
+ nk.num_values = 0;
+ nk.values_offset = -1;
+ memset(nk.unk3, 0, sizeof(nk.unk3));
+ nk.clsname_offset = -1; /* FIXME: fill in */
+ nk.clsname_length = 0;
+ nk.key_name = name;
+
+ /* Get the security descriptor of the root key */
+ root = talloc_zero(ctx, struct nk_block);
+ W_ERROR_HAVE_NO_MEMORY(root);
+
+ if (!hbin_get_tdr(regf, regf->header->data_offset, root,
+ (tdr_pull_fn_t)tdr_pull_nk_block, root)) {
+ DEBUG(0, ("Unable to find HBIN data for offset 0x%x\n",
+ regf->header->data_offset));
+ return WERR_GEN_FAILURE;
+ }
+ nk.sk_offset = root->sk_offset;
+ talloc_free(root);
+
+ /* Store the new nk key */
+ offset = hbin_store_tdr(regf, (tdr_push_fn_t) tdr_push_nk_block, &nk);
+
+ error = regf_sl_add_entry(regf, parent_nk->subkeys_offset, name, offset,
+ &parent_nk->subkeys_offset);
+ if (!W_ERROR_IS_OK(error)) {
+ hbin_free(regf, offset);
+ return error;
+ }
+
+ parent_nk->num_subkeys++;
+
+ /* Since the subkey offset of the parent can change, store it again */
+ hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_nk_block,
+ nk.parent_offset, parent_nk);
+
+ *ret = (struct hive_key *)regf_get_key(ctx, regf, offset);
+
+ DEBUG(9, ("Storing key %s\n", name));
+ return regf_save_hbin(private_data->hive, 0);
+}
+
+static WERROR regf_set_value(struct hive_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data)
+{
+ struct regf_key_data *private_data = (struct regf_key_data *)key;
+ struct regf_data *regf = private_data->hive;
+ struct nk_block *nk = private_data->nk;
+ struct vk_block vk;
+ uint32_t i;
+ uint32_t tmp_vk_offset, vk_offset, old_vk_offset = (uint32_t) -1;
+ DATA_BLOB values = {0};
+
+ ZERO_STRUCT(vk);
+
+ /* find the value offset, if it exists */
+ if (nk->values_offset != -1) {
+ values = hbin_get(regf, nk->values_offset);
+
+ for (i = 0; i < nk->num_values; i++) {
+ tmp_vk_offset = IVAL(values.data, i * 4);
+ if (!hbin_get_tdr(regf, tmp_vk_offset, private_data,
+ (tdr_pull_fn_t)tdr_pull_vk_block,
+ &vk)) {
+ DEBUG(0, ("Unable to get VK block at 0x%x\n",
+ tmp_vk_offset));
+ return WERR_GEN_FAILURE;
+ }
+ if (strcmp(vk.data_name, name) == 0) {
+ old_vk_offset = tmp_vk_offset;
+ break;
+ }
+ }
+ }
+
+ /* If it's new, create the vk struct, if it's old, free the old data. */
+ if (old_vk_offset == -1) {
+ vk.header = "vk";
+ if (name != NULL && name[0] != '\0') {
+ vk.flag = 1;
+ vk.data_name = name;
+ vk.name_length = strlen(name);
+ } else {
+ vk.flag = 0;
+ vk.data_name = NULL;
+ vk.name_length = 0;
+ }
+ } else {
+ /* Free data, if any */
+ if (!(vk.data_length & 0x80000000)) {
+ hbin_free(regf, vk.data_offset);
+ }
+ }
+
+ /* Set the type and data */
+ vk.data_length = data.length;
+ vk.data_type = type;
+ if ((type == REG_DWORD) || (type == REG_DWORD_BIG_ENDIAN)) {
+ if (vk.data_length != sizeof(uint32_t)) {
+ DEBUG(0, ("DWORD or DWORD_BIG_ENDIAN value with size other than 4 byte!\n"));
+ return WERR_NOT_SUPPORTED;
+ }
+ vk.data_length |= 0x80000000;
+ vk.data_offset = IVAL(data.data, 0);
+ } else {
+ /* Store data somewhere */
+ vk.data_offset = hbin_store(regf, data);
+ }
+ if (old_vk_offset == -1) {
+ /* Store new vk */
+ vk_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_vk_block,
+ &vk);
+ } else {
+ /* Store vk at offset */
+ vk_offset = hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_vk_block,
+ old_vk_offset ,&vk);
+ }
+
+ /* Re-allocate the value list */
+ if (nk->values_offset == -1) {
+ nk->values_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_uint32,
+ &vk_offset);
+ nk->num_values = 1;
+ } else {
+
+ /* Change if we're changing, otherwise we're adding the value */
+ if (old_vk_offset != -1) {
+ /* Find and overwrite the offset. */
+ for (i = 0; i < nk->num_values; i++) {
+ if (IVAL(values.data, i * 4) == old_vk_offset) {
+ SIVAL(values.data, i * 4, vk_offset);
+ break;
+ }
+ }
+ } else {
+ /* Create a new value list */
+ DATA_BLOB value_list;
+
+ value_list.length = (nk->num_values+1)*4;
+ value_list.data = (uint8_t *)talloc_array(private_data,
+ uint32_t,
+ nk->num_values+1);
+ W_ERROR_HAVE_NO_MEMORY(value_list.data);
+ memcpy(value_list.data, values.data, nk->num_values * 4);
+
+ SIVAL(value_list.data, nk->num_values * 4, vk_offset);
+ nk->num_values++;
+ nk->values_offset = hbin_store_resize(regf,
+ nk->values_offset,
+ value_list);
+ }
+
+ }
+ hbin_store_tdr_resize(regf,
+ (tdr_push_fn_t) tdr_push_nk_block,
+ private_data->offset, nk);
+ return regf_save_hbin(private_data->hive, 0);
+}
+
+static WERROR regf_save_hbin(struct regf_data *regf, bool flush)
+{
+ struct tdr_push *push = tdr_push_init(regf);
+ unsigned int i;
+
+ W_ERROR_HAVE_NO_MEMORY(push);
+
+ /* Only write once every 5 seconds, or when flush is set */
+ if (!flush && regf->last_write + 5 >= time(NULL)) {
+ return WERR_OK;
+ }
+
+ regf->last_write = time(NULL);
+
+ if (lseek(regf->fd, 0, SEEK_SET) == -1) {
+ DEBUG(0, ("Error lseeking in regf file\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ /* Recompute checksum */
+ if (NT_STATUS_IS_ERR(tdr_push_regf_hdr(push, regf->header))) {
+ DEBUG(0, ("Failed to push regf header\n"));
+ return WERR_GEN_FAILURE;
+ }
+ regf->header->chksum = regf_hdr_checksum(push->data.data);
+ talloc_free(push);
+
+ if (NT_STATUS_IS_ERR(tdr_push_to_fd(regf->fd,
+ (tdr_push_fn_t)tdr_push_regf_hdr,
+ regf->header))) {
+ DEBUG(0, ("Error writing registry file header\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ if (lseek(regf->fd, 0x1000, SEEK_SET) == -1) {
+ DEBUG(0, ("Error lseeking to 0x1000 in regf file\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ for (i = 0; regf->hbins[i]; i++) {
+ if (NT_STATUS_IS_ERR(tdr_push_to_fd(regf->fd,
+ (tdr_push_fn_t)tdr_push_hbin_block,
+ regf->hbins[i]))) {
+ DEBUG(0, ("Error writing HBIN block\n"));
+ return WERR_GEN_FAILURE;
+ }
+ }
+
+ return WERR_OK;
+}
+
+WERROR reg_create_regf_file(TALLOC_CTX *parent_ctx,
+ const char *location,
+ int minor_version, struct hive_key **key)
+{
+ struct regf_data *regf;
+ struct regf_hdr *regf_hdr;
+ struct nk_block nk;
+ struct sk_block sk;
+ WERROR error;
+ DATA_BLOB data;
+ struct security_descriptor *sd;
+ uint32_t sk_offset;
+
+ regf = (struct regf_data *)talloc_zero(NULL, struct regf_data);
+
+ W_ERROR_HAVE_NO_MEMORY(regf);
+
+ DEBUG(5, ("Attempting to create registry file\n"));
+
+ /* Get the header */
+ regf->fd = creat(location, 0644);
+
+ if (regf->fd == -1) {
+ DEBUG(0,("Could not create file: %s, %s\n", location,
+ strerror(errno)));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ regf_hdr = talloc_zero(regf, struct regf_hdr);
+ W_ERROR_HAVE_NO_MEMORY(regf_hdr);
+ regf_hdr->REGF_ID = "regf";
+ unix_to_nt_time(&regf_hdr->modtime, time(NULL));
+ regf_hdr->version.major = 1;
+ regf_hdr->version.minor = minor_version;
+ regf_hdr->last_block = 0x1000; /* Block size */
+ regf_hdr->description = talloc_strdup(regf_hdr,
+ "Registry created by Samba 4");
+ W_ERROR_HAVE_NO_MEMORY(regf_hdr->description);
+ regf_hdr->chksum = 0;
+
+ regf->header = regf_hdr;
+
+ /* Create all hbin blocks */
+ regf->hbins = talloc_array(regf, struct hbin_block *, 1);
+ W_ERROR_HAVE_NO_MEMORY(regf->hbins);
+ regf->hbins[0] = NULL;
+
+ nk.header = "nk";
+ nk.type = REG_ROOT_KEY;
+ unix_to_nt_time(&nk.last_change, time(NULL));
+ nk.uk1 = 0;
+ nk.parent_offset = -1;
+ nk.num_subkeys = 0;
+ nk.uk2 = 0;
+ nk.subkeys_offset = -1;
+ nk.unknown_offset = -1;
+ nk.num_values = 0;
+ nk.values_offset = -1;
+ memset(nk.unk3, 0, 5 * sizeof(uint32_t));
+ nk.clsname_offset = -1;
+ nk.clsname_length = 0;
+ nk.sk_offset = 0x80;
+ nk.key_name = "SambaRootKey";
+
+ /*
+ * It should be noted that changing the key_name to something shorter
+ * creates a shorter nk block, which makes the position of the sk block
+ * change. All Windows registries I've seen have the sk at 0x80.
+ * I therefore recommend that our regf files share that offset -- Wilco
+ */
+
+ /* Create a security descriptor. */
+ sd = security_descriptor_dacl_create(regf,
+ 0,
+ NULL, NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+
+ /* Push the security descriptor to a blob */
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_struct_blob(&data, regf,
+ sd, (ndr_push_flags_fn_t)ndr_push_security_descriptor))) {
+ DEBUG(0, ("Unable to push security descriptor\n"));
+ return WERR_GEN_FAILURE;
+ }
+
+ ZERO_STRUCT(sk);
+ sk.header = "sk";
+ sk.prev_offset = 0x80;
+ sk.next_offset = 0x80;
+ sk.ref_cnt = 1;
+ sk.rec_size = data.length;
+ sk.sec_desc = data.data;
+
+ /* Store the new nk key */
+ regf->header->data_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t)tdr_push_nk_block,
+ &nk);
+ /* Store the sk block */
+ sk_offset = hbin_store_tdr(regf,
+ (tdr_push_fn_t) tdr_push_sk_block,
+ &sk);
+ if (sk_offset != 0x80) {
+ DEBUG(0, ("Error storing sk block, should be at 0x80, stored at 0x%x\n", nk.sk_offset));
+ return WERR_GEN_FAILURE;
+ }
+
+
+ *key = (struct hive_key *)regf_get_key(parent_ctx, regf,
+ regf->header->data_offset);
+
+ error = regf_save_hbin(regf, 1);
+ if (!W_ERROR_IS_OK(error)) {
+ return error;
+ }
+
+ /* We can drop our own reference now that *key will have created one */
+ talloc_unlink(NULL, regf);
+
+ return WERR_OK;
+}
+
+static WERROR regf_flush_key(struct hive_key *key)
+{
+ struct regf_key_data *private_data = (struct regf_key_data *)key;
+ struct regf_data *regf = private_data->hive;
+ WERROR error;
+
+ error = regf_save_hbin(regf, 1);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Failed to flush regf to disk\n"));
+ return error;
+ }
+
+ return WERR_OK;
+}
+
+static int regf_destruct(struct regf_data *regf)
+{
+ WERROR error;
+
+ /* Write to disk */
+ error = regf_save_hbin(regf, 1);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Failed to flush registry to disk\n"));
+ return -1;
+ }
+
+ /* Close file descriptor */
+ close(regf->fd);
+
+ return 0;
+}
+
+WERROR reg_open_regf_file(TALLOC_CTX *parent_ctx, const char *location,
+ struct hive_key **key)
+{
+ struct regf_data *regf;
+ struct regf_hdr *regf_hdr;
+ struct tdr_pull *pull;
+ unsigned int i;
+
+ regf = (struct regf_data *)talloc_zero(parent_ctx, struct regf_data);
+ W_ERROR_HAVE_NO_MEMORY(regf);
+
+ talloc_set_destructor(regf, regf_destruct);
+
+ DEBUG(5, ("Attempting to load registry file\n"));
+
+ /* Get the header */
+ regf->fd = open(location, O_RDWR);
+
+ if (regf->fd == -1) {
+ DEBUG(0,("Could not load file: %s, %s\n", location,
+ strerror(errno)));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ pull = tdr_pull_init(regf);
+
+ pull->data.data = (uint8_t*)fd_load(regf->fd, &pull->data.length, 0, regf);
+
+ if (pull->data.data == NULL) {
+ DEBUG(0, ("Error reading data from file: %s\n", location));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ regf_hdr = talloc(regf, struct regf_hdr);
+ W_ERROR_HAVE_NO_MEMORY(regf_hdr);
+
+ if (NT_STATUS_IS_ERR(tdr_pull_regf_hdr(pull, regf_hdr, regf_hdr))) {
+ DEBUG(0, ("Failed to pull regf header from file: %s\n", location));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ regf->header = regf_hdr;
+
+ if (strcmp(regf_hdr->REGF_ID, "regf") != 0) {
+ DEBUG(0, ("Unrecognized NT registry header id: %s, %s\n",
+ regf_hdr->REGF_ID, location));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ /* Validate the header ... */
+ if (regf_hdr_checksum(pull->data.data) != regf_hdr->chksum) {
+ DEBUG(0, ("Registry file checksum error: %s: %d,%d\n",
+ location, regf_hdr->chksum,
+ regf_hdr_checksum(pull->data.data)));
+ talloc_free(regf);
+ return WERR_GEN_FAILURE;
+ }
+
+ pull->offset = 0x1000;
+
+ i = 0;
+ /* Read in all hbin blocks */
+ regf->hbins = talloc_array(regf, struct hbin_block *, 1);
+ W_ERROR_HAVE_NO_MEMORY(regf->hbins);
+
+ regf->hbins[0] = NULL;
+
+ while (pull->offset < pull->data.length &&
+ pull->offset <= regf->header->last_block) {
+ struct hbin_block *hbin = talloc(regf->hbins,
+ struct hbin_block);
+
+ W_ERROR_HAVE_NO_MEMORY(hbin);
+
+ if (NT_STATUS_IS_ERR(tdr_pull_hbin_block(pull, hbin, hbin))) {
+ DEBUG(0, ("[%d] Error parsing HBIN block\n", i));
+ talloc_free(regf);
+ return WERR_FOOBAR;
+ }
+
+ if (strcmp(hbin->HBIN_ID, "hbin") != 0) {
+ DEBUG(0, ("[%d] Expected 'hbin', got '%s'\n",
+ i, hbin->HBIN_ID));
+ talloc_free(regf);
+ return WERR_FOOBAR;
+ }
+
+ regf->hbins[i] = hbin;
+ i++;
+ regf->hbins = talloc_realloc(regf, regf->hbins,
+ struct hbin_block *, i+2);
+ regf->hbins[i] = NULL;
+ }
+
+ talloc_free(pull);
+
+ DEBUG(1, ("%d HBIN blocks read\n", i));
+
+ *key = (struct hive_key *)regf_get_key(parent_ctx, regf,
+ regf->header->data_offset);
+
+ /* We can drop our own reference now that *key will have created one */
+ talloc_unlink(parent_ctx, regf);
+
+ return WERR_OK;
+}
+
+static struct hive_operations reg_backend_regf = {
+ .name = "regf",
+ .get_key_info = regf_get_info,
+ .enum_key = regf_get_subkey_by_index,
+ .get_key_by_name = regf_get_subkey_by_name,
+ .get_value_by_name = regf_get_value_by_name,
+ .enum_value = regf_get_value,
+ .get_sec_desc = regf_get_sec_desc,
+ .set_sec_desc = regf_set_sec_desc,
+ .add_key = regf_add_key,
+ .set_value = regf_set_value,
+ .del_key = regf_del_key,
+ .delete_value = regf_del_value,
+ .flush_key = regf_flush_key
+};
diff --git a/source4/lib/registry/regf.idl b/source4/lib/registry/regf.idl
new file mode 100644
index 0000000..064aaf0
--- /dev/null
+++ b/source4/lib/registry/regf.idl
@@ -0,0 +1,167 @@
+/*
+ Definitions for the REGF registry file format as used by
+ Windows NT4 and above.
+
+ Copyright (C) 2005 Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2006 Wilco Baan Hofman, wilco@baanhofman.nl
+
+ Based on two files from Samba 3:
+ regedit.c by Richard Sharpe
+ regfio.c by Jerry Carter
+
+*/
+
+interface regf
+{
+ const int REGF_OFFSET_NONE = 0xffffffff;
+
+ /*
+ * Registry version number
+ * 1.2.0.1 for WinNT 3.51
+ * 1.3.0.1 for WinNT 4
+ * 1.5.0.1 for WinXP
+ */
+
+ [noprint] struct regf_version {
+ [value(1)] uint32 major;
+ uint32 minor;
+ [value(0)] uint32 release;
+ [value(1)] uint32 build;
+ };
+
+ /*
+ "regf" is obviously the abbreviation for "Registry file". "regf" is the
+ signature of the header-block which is always 4kb in size, although only
+ the first 64 bytes seem to be used and a checksum is calculated over
+ the first 0x200 bytes only!
+ */
+
+ [public,noprint] struct regf_hdr {
+ [charset(DOS)] uint8 REGF_ID[4]; /* 'regf' */
+ uint32 update_counter1;
+ uint32 update_counter2;
+ NTTIME modtime;
+ regf_version version;
+ uint32 data_offset;
+ uint32 last_block;
+ [value(1)] uint32 uk7; /* 1 */
+ [charset(UTF16)] uint16 description[0x20];
+ uint32 padding[99]; /* Padding */
+ /* Checksum of first 0x200 bytes XOR-ed */
+ uint32 chksum;
+ };
+
+ /*
+ hbin probably means hive-bin (i.e. hive-container)
+ This block is always a multiple
+ of 4kb in size.
+ */
+ [public,noprint] struct hbin_block {
+ [charset(DOS)] uint8 HBIN_ID[4]; /* hbin */
+ uint32 offset_from_first; /* Offset from 1st hbin-Block */
+ uint32 offset_to_next; /* Offset to the next hbin-Block */
+ uint32 unknown[2];
+ NTTIME last_change;
+ uint32 block_size; /* Block size (including the header!) */
+ uint8 data[offset_to_next-0x20];
+ /* data is filled with:
+ uint32 length;
+ Negative if in use, positive otherwise
+ Always a multiple of 8
+ uint8_t data[length];
+ Free space marker if 0xffffffff
+ */
+ };
+
+ [noprint] enum reg_key_type {
+ REG_ROOT_KEY = 0x2C,
+ REG_SUB_KEY = 0x20,
+ REG_SYM_LINK = 0x10
+ };
+
+ /*
+ The nk-record can be treated as a combination of tree-record and
+ key-record of the win 95 registry.
+ */
+ [public,noprint] struct nk_block {
+ [charset(DOS)] uint8 header[2];
+ reg_key_type type;
+ NTTIME last_change;
+ uint32 uk1;
+ uint32 parent_offset;
+ uint32 num_subkeys;
+ uint32 uk2;
+ uint32 subkeys_offset;
+ uint32 unknown_offset;
+ uint32 num_values;
+ uint32 values_offset; /* Points to a list of offsets of vk-records */
+ uint32 sk_offset;
+ uint32 clsname_offset;
+ uint32 unk3[5];
+ [value(strlen(key_name))] uint16 name_length;
+ uint16 clsname_length;
+ [charset(DOS)] uint8 key_name[name_length];
+ };
+
+ /* sk (? Security Key ?) is the ACL of the registry. */
+ [noprint,public] struct sk_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 tag;
+ uint32 prev_offset;
+ uint32 next_offset;
+ uint32 ref_cnt;
+ uint32 rec_size;
+ uint8 sec_desc[rec_size];
+ };
+
+ [noprint] struct lh_hash {
+ uint32 nk_offset;
+ uint32 base37; /* base37 of key name */
+ };
+
+ /* Subkey listing with hash of first 4 characters */
+ [public,noprint] struct lh_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ lh_hash hr[key_count];
+ };
+
+ [public,noprint] struct li_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ uint32 nk_offset[key_count];
+ };
+
+ [public,noprint] struct ri_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ uint32 offset[key_count]; /* li/lh offset */
+ };
+
+ /* The vk-record consists information to a single value (value key). */
+ [public,noprint] struct vk_block {
+ [charset(DOS)] uint8 header[2];
+ [value(strlen(data_name))] uint16 name_length;
+ uint32 data_length; /* If top-bit set, offset contains the data */
+ uint32 data_offset;
+ uint32 data_type;
+ uint16 flag; /* =1, has name, else no name (=Default). */
+ uint16 unk1;
+ [charset(DOS)] uint8 data_name[name_length];
+ };
+
+ [noprint] struct hash_record {
+ uint32 nk_offset;
+ [charset(DOS)] uint8 hash[4];
+ };
+
+ /*
+ The lf-record is the counterpart to the RGKN-record (the
+ hash-function)
+ */
+ [public,noprint] struct lf_block {
+ [charset(DOS)] uint8 header[2];
+ uint16 key_count;
+ hash_record hr[key_count]; /* Array of hash records, depending on key_count */
+ };
+}
diff --git a/source4/lib/registry/registry.h b/source4/lib/registry/registry.h
new file mode 100644
index 0000000..c22038c
--- /dev/null
+++ b/source4/lib/registry/registry.h
@@ -0,0 +1,532 @@
+/*
+ Unix SMB/CIFS implementation.
+ Registry interface
+ Copyright (C) Gerald Carter 2002.
+ Copyright (C) Jelmer Vernooij 2003-2007.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _REGISTRY_H /* _REGISTRY_H */
+#define _REGISTRY_H
+
+struct registry_context;
+struct loadparm_context;
+
+#include <talloc.h>
+#include "libcli/util/werror.h"
+#include "librpc/gen_ndr/security.h"
+#include "libcli/util/ntstatus.h"
+#include "../lib/util/time.h"
+#include "../lib/util/data_blob.h"
+
+/**
+ * The hive API. This API is generally used for
+ * reading a specific file that contains just one hive.
+ *
+ * Good examples are .DAT (NTUSER.DAT) files.
+ *
+ * This API does not have any notification support (that
+ * should be provided by the registry implementation), nor
+ * does it understand what predefined keys are.
+ */
+
+struct hive_key {
+ const struct hive_operations *ops;
+};
+
+struct hive_operations {
+ const char *name;
+
+ /**
+ * Open a specific subkey
+ */
+ WERROR (*enum_key) (TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time);
+
+ /**
+ * Open a subkey by name
+ */
+ WERROR (*get_key_by_name) (TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, const char *name,
+ struct hive_key **subkey);
+
+ /**
+ * Add a new key.
+ */
+ WERROR (*add_key) (TALLOC_CTX *ctx,
+ const struct hive_key *parent_key, const char *path,
+ const char *classname,
+ struct security_descriptor *desc,
+ struct hive_key **key);
+ /**
+ * Remove an existing key.
+ */
+ WERROR (*del_key) (TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, const char *name);
+
+ /**
+ * Force write of a key to disk.
+ */
+ WERROR (*flush_key) (struct hive_key *key);
+
+ /**
+ * Retrieve a registry value with a specific index.
+ */
+ WERROR (*enum_value) (TALLOC_CTX *mem_ctx,
+ struct hive_key *key, uint32_t idx,
+ const char **name, uint32_t *type,
+ DATA_BLOB *data);
+
+ /**
+ * Retrieve a registry value with the specified name
+ */
+ WERROR (*get_value_by_name) (TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data);
+
+ /**
+ * Set a value on the specified registry key.
+ */
+ WERROR (*set_value) (struct hive_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data);
+
+ /**
+ * Remove a value.
+ */
+ WERROR (*delete_value) (TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name);
+
+ /* Security Descriptors */
+
+ /**
+ * Change the security descriptor on a registry key.
+ *
+ * This should return WERR_NOT_SUPPORTED if the underlying
+ * format does not have a mechanism for storing
+ * security descriptors.
+ */
+ WERROR (*set_sec_desc) (struct hive_key *key,
+ const struct security_descriptor *desc);
+
+ /**
+ * Retrieve the security descriptor on a registry key.
+ *
+ * This should return WERR_NOT_SUPPORTED if the underlying
+ * format does not have a mechanism for storing
+ * security descriptors.
+ */
+ WERROR (*get_sec_desc) (TALLOC_CTX *ctx,
+ const struct hive_key *key,
+ struct security_descriptor **desc);
+
+ /**
+ * Retrieve general information about a key.
+ */
+ WERROR (*get_key_info) (TALLOC_CTX *mem_ctx,
+ const struct hive_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize);
+};
+
+struct cli_credentials;
+struct auth_session_info;
+struct tevent_context;
+
+WERROR reg_open_hive(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **root);
+WERROR hive_key_get_info(TALLOC_CTX *mem_ctx, const struct hive_key *key,
+ const char **classname, uint32_t *num_subkeys,
+ uint32_t *num_values, NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen, uint32_t *max_valbufsize);
+WERROR hive_key_add_name(TALLOC_CTX *ctx, const struct hive_key *parent_key,
+ const char *name, const char *classname,
+ struct security_descriptor *desc,
+ struct hive_key **key);
+WERROR hive_key_del(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, const char *name);
+WERROR hive_get_key_by_name(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, const char *name,
+ struct hive_key **subkey);
+WERROR hive_enum_key(TALLOC_CTX *mem_ctx,
+ const struct hive_key *key, uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time);
+
+WERROR hive_key_set_value(struct hive_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data);
+
+WERROR hive_get_value(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name,
+ uint32_t *type, DATA_BLOB *data);
+WERROR hive_get_value_by_index(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type, DATA_BLOB *data);
+WERROR hive_get_sec_desc(TALLOC_CTX *mem_ctx,
+ struct hive_key *key,
+ struct security_descriptor **security);
+
+WERROR hive_set_sec_desc(struct hive_key *key,
+ const struct security_descriptor *security);
+
+WERROR hive_key_del_value(TALLOC_CTX *mem_ctx,
+ struct hive_key *key, const char *name);
+
+WERROR hive_key_flush(struct hive_key *key);
+
+
+/* Individual backends */
+WERROR reg_open_directory(TALLOC_CTX *parent_ctx,
+ const char *location, struct hive_key **key);
+WERROR reg_open_regf_file(TALLOC_CTX *parent_ctx,
+ const char *location, struct hive_key **key);
+WERROR reg_open_ldb_file(TALLOC_CTX *parent_ctx, const char *location,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct hive_key **k);
+
+
+WERROR reg_create_directory(TALLOC_CTX *parent_ctx,
+ const char *location, struct hive_key **key);
+WERROR reg_create_regf_file(TALLOC_CTX *parent_ctx,
+ const char *location,
+ int major_version,
+ struct hive_key **key);
+
+
+
+/* Handles for the predefined keys */
+#define HKEY_CLASSES_ROOT 0x80000000
+#define HKEY_CURRENT_USER 0x80000001
+#define HKEY_LOCAL_MACHINE 0x80000002
+#define HKEY_USERS 0x80000003
+#define HKEY_PERFORMANCE_DATA 0x80000004
+#define HKEY_CURRENT_CONFIG 0x80000005
+#define HKEY_DYN_DATA 0x80000006
+#define HKEY_PERFORMANCE_TEXT 0x80000050
+#define HKEY_PERFORMANCE_NLSTEXT 0x80000060
+
+#define HKEY_FIRST HKEY_CLASSES_ROOT
+#define HKEY_LAST HKEY_PERFORMANCE_NLSTEXT
+
+struct reg_predefined_key {
+ uint32_t handle;
+ const char *name;
+};
+
+extern const struct reg_predefined_key reg_predefined_keys[];
+
+#define REG_DELETE -1
+
+/*
+ * The general idea here is that every backend provides a 'hive'. Combining
+ * various hives gives you a complete registry like windows has
+ */
+
+#define REGISTRY_INTERFACE_VERSION 1
+
+struct reg_key_operations;
+
+/* structure to store the registry handles */
+struct registry_key
+{
+ struct registry_context *context;
+};
+
+struct registry_value
+{
+ const char *name;
+ unsigned int data_type;
+ DATA_BLOB data;
+};
+
+/* FIXME */
+typedef void (*reg_key_notification_function) (void);
+typedef void (*reg_value_notification_function) (void);
+
+struct cli_credentials;
+
+struct registry_operations {
+ const char *name;
+
+ WERROR (*get_key_info) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **classname,
+ uint32_t *numsubkeys,
+ uint32_t *numvalues,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize);
+
+ WERROR (*flush_key) (struct registry_key *key);
+
+ WERROR (*get_predefined_key) (struct registry_context *ctx,
+ uint32_t key_id,
+ struct registry_key **key);
+
+ WERROR (*open_key) (TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *path,
+ struct registry_key **key);
+
+ WERROR (*create_key) (TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *name,
+ const char *key_class,
+ struct security_descriptor *security,
+ struct registry_key **key);
+
+ WERROR (*delete_key) (TALLOC_CTX *mem_ctx,
+ struct registry_key *key, const char *name);
+
+ WERROR (*delete_value) (TALLOC_CTX *mem_ctx,
+ struct registry_key *key, const char *name);
+
+ WERROR (*enum_key) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time);
+
+ WERROR (*enum_value) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type,
+ DATA_BLOB *data);
+
+ WERROR (*get_sec_desc) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ struct security_descriptor **security);
+
+ WERROR (*set_sec_desc) (struct registry_key *key,
+ const struct security_descriptor *security);
+
+ WERROR (*load_key) (struct registry_key *key,
+ const char *key_name,
+ const char *path);
+
+ WERROR (*unload_key) (struct registry_key *key, const char *name);
+
+ WERROR (*notify_value_change) (struct registry_key *key,
+ reg_value_notification_function fn);
+
+ WERROR (*get_value) (TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ uint32_t *type,
+ DATA_BLOB *data);
+
+ WERROR (*set_value) (struct registry_key *key,
+ const char *name,
+ uint32_t type,
+ const DATA_BLOB data);
+};
+
+/**
+ * Handle to a full registry
+ * contains zero or more hives
+ */
+struct registry_context {
+ const struct registry_operations *ops;
+};
+
+struct auth_session_info;
+struct tevent_context;
+struct loadparm_context;
+
+/**
+ * Open the locally defined registry.
+ */
+WERROR reg_open_local(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx);
+
+WERROR reg_open_samba(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials);
+
+/**
+ * Open the registry on a remote machine.
+ */
+WERROR reg_open_remote(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ const char *location, struct tevent_context *ev);
+
+WERROR reg_open_wine(struct registry_context **ctx, const char *path);
+
+const char *reg_get_predef_name(uint32_t hkey);
+WERROR reg_get_predefined_key_by_name(struct registry_context *ctx,
+ const char *name,
+ struct registry_key **key);
+WERROR reg_get_predefined_key(struct registry_context *ctx,
+ uint32_t hkey,
+ struct registry_key **key);
+
+WERROR reg_open_key(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name, struct registry_key **result);
+
+WERROR reg_key_get_value_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key, uint32_t idx,
+ const char **name,
+ uint32_t *type,
+ DATA_BLOB *data);
+WERROR reg_key_get_info(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **class_name,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_change_time,
+ uint32_t *max_subkeynamelen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize);
+WERROR reg_key_get_subkey_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ uint32_t idx,
+ const char **name,
+ const char **classname,
+ NTTIME *last_mod_time);
+WERROR reg_key_get_subkey_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ struct registry_key **subkey);
+WERROR reg_key_get_value_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char *name,
+ uint32_t *type,
+ DATA_BLOB *data);
+WERROR reg_key_del(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent, const char *name);
+WERROR reg_key_add_name(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent, const char *name,
+ const char *classname,
+ struct security_descriptor *desc,
+ struct registry_key **newkey);
+WERROR reg_val_set(struct registry_key *key, const char *value,
+ uint32_t type, DATA_BLOB data);
+WERROR reg_get_sec_desc(TALLOC_CTX *ctx, const struct registry_key *key,
+ struct security_descriptor **secdesc);
+WERROR reg_del_value(TALLOC_CTX *mem_ctx,
+ struct registry_key *key, const char *valname);
+WERROR reg_key_flush(struct registry_key *key);
+WERROR reg_create_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent,
+ const char *name,
+ const char *key_class,
+ struct security_descriptor *security,
+ struct registry_key **key);
+
+/* Utility functions */
+const char *str_regtype(int type);
+bool push_reg_sz(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, const char *s);
+bool push_reg_multi_sz(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, const char **a);
+bool pull_reg_sz(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, const char **s);
+bool pull_reg_multi_sz(TALLOC_CTX *mem_ctx, const DATA_BLOB *blob, const char ***a);
+int regtype_by_string(const char *str);
+char *reg_val_data_string(TALLOC_CTX *mem_ctx, uint32_t type, const DATA_BLOB data);
+char *reg_val_description(TALLOC_CTX *mem_ctx, const char *name,
+ uint32_t type, const DATA_BLOB data);
+bool reg_string_to_val(TALLOC_CTX *mem_ctx, const char *type_str,
+ const char *data_str, uint32_t *type, DATA_BLOB *data);
+WERROR reg_open_key_abs(TALLOC_CTX *mem_ctx, struct registry_context *handle,
+ const char *name, struct registry_key **result);
+WERROR reg_key_del_abs(struct registry_context *ctx, const char *path);
+WERROR reg_key_add_abs(TALLOC_CTX *mem_ctx, struct registry_context *ctx,
+ const char *path, uint32_t access_mask,
+ struct security_descriptor *sec_desc,
+ struct registry_key **result);
+WERROR reg_load_key(struct registry_context *ctx, struct registry_key *key,
+ const char *name, const char *filename);
+
+WERROR reg_mount_hive(struct registry_context *rctx,
+ struct hive_key *hive_key,
+ uint32_t key_id,
+ const char **elements);
+
+struct registry_key *reg_import_hive_key(struct registry_context *ctx,
+ struct hive_key *hive,
+ uint32_t predef_key,
+ const char **elements);
+WERROR reg_set_sec_desc(struct registry_key *key,
+ const struct security_descriptor *security);
+
+struct reg_diff_callbacks {
+ WERROR (*add_key) (void *callback_data, const char *key_name);
+ WERROR (*set_value) (void *callback_data, const char *key_name,
+ const char *value_name, uint32_t value_type,
+ DATA_BLOB value);
+ WERROR (*del_value) (void *callback_data, const char *key_name,
+ const char *value_name);
+ WERROR (*del_key) (void *callback_data, const char *key_name);
+ WERROR (*del_all_values) (void *callback_data, const char *key_name);
+ WERROR (*done) (void *callback_data);
+};
+
+WERROR reg_diff_apply(struct registry_context *ctx,
+ const char *filename);
+
+WERROR reg_generate_diff(struct registry_context *ctx1,
+ struct registry_context *ctx2,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+WERROR reg_dotreg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data);
+WERROR reg_preg_diff_save(TALLOC_CTX *ctx, const char *filename,
+ struct reg_diff_callbacks **callbacks,
+ void **callback_data);
+WERROR reg_generate_diff_key(struct registry_key *oldkey,
+ struct registry_key *newkey,
+ const char *path,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+WERROR reg_diff_load(const char *filename,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+WERROR reg_dotreg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+WERROR reg_preg_diff_load(int fd,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+WERROR local_get_predefined_key(struct registry_context *ctx,
+ uint32_t key_id, struct registry_key **key);
+
+
+#endif /* _REGISTRY_H */
diff --git a/source4/lib/registry/rpc.c b/source4/lib/registry/rpc.c
new file mode 100644
index 0000000..2f6edf3
--- /dev/null
+++ b/source4/lib/registry/rpc.c
@@ -0,0 +1,579 @@
+/*
+ Samba Unix/Linux SMB implementation
+ RPC backend for the registry library
+ Copyright (C) 2003-2007 Jelmer Vernooij, jelmer@samba.org
+ Copyright (C) 2008 Matthias Dieter Wallnöfer, mwallnoefer@yahoo.de
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "registry.h"
+#include "librpc/gen_ndr/ndr_winreg_c.h"
+
+#define MAX_NAMESIZE 512
+#define MAX_VALSIZE 32768
+
+struct rpc_key {
+ struct registry_key key;
+ struct policy_handle pol;
+ struct dcerpc_binding_handle *binding_handle;
+ const char* classname;
+ uint32_t num_subkeys;
+ uint32_t max_subkeylen;
+ uint32_t max_classlen;
+ uint32_t num_values;
+ uint32_t max_valnamelen;
+ uint32_t max_valbufsize;
+ uint32_t secdescsize;
+ NTTIME last_changed_time;
+};
+
+struct rpc_registry_context {
+ struct registry_context context;
+ struct dcerpc_pipe *pipe;
+ struct dcerpc_binding_handle *binding_handle;
+};
+
+static struct registry_operations reg_backend_rpc;
+
+/**
+ * This is the RPC backend for the registry library.
+ */
+
+#define openhive(u) static WERROR open_ ## u(struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx, struct policy_handle *hnd) \
+{ \
+ struct winreg_Open ## u r; \
+ NTSTATUS status; \
+\
+ ZERO_STRUCT(r); \
+ r.in.system_name = NULL; \
+ r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; \
+ r.out.handle = hnd;\
+\
+ status = dcerpc_winreg_Open ## u ## _r(b, mem_ctx, &r); \
+\
+ if (!NT_STATUS_IS_OK(status)) { \
+ DEBUG(1, ("OpenHive failed - %s\n", nt_errstr(status))); \
+ return ntstatus_to_werror(status); \
+ } \
+\
+ return r.out.result;\
+}
+
+openhive(HKLM)
+openhive(HKCU)
+openhive(HKPD)
+openhive(HKU)
+openhive(HKCR)
+openhive(HKDD)
+openhive(HKCC)
+
+static struct {
+ uint32_t hkey;
+ WERROR (*open) (struct dcerpc_binding_handle *b, TALLOC_CTX *,
+ struct policy_handle *h);
+} known_hives[] = {
+ { HKEY_LOCAL_MACHINE, open_HKLM },
+ { HKEY_CURRENT_USER, open_HKCU },
+ { HKEY_CLASSES_ROOT, open_HKCR },
+ { HKEY_PERFORMANCE_DATA, open_HKPD },
+ { HKEY_USERS, open_HKU },
+ { HKEY_DYN_DATA, open_HKDD },
+ { HKEY_CURRENT_CONFIG, open_HKCC },
+ { 0, NULL }
+};
+
+static WERROR rpc_query_key(TALLOC_CTX *mem_ctx, const struct registry_key *k);
+
+static WERROR rpc_get_predefined_key(struct registry_context *ctx,
+ uint32_t hkey_type,
+ struct registry_key **k)
+{
+ int n;
+ struct rpc_key *mykeydata;
+ struct rpc_registry_context *rctx = talloc_get_type(ctx, struct rpc_registry_context);
+
+ *k = NULL;
+
+ for(n = 0; known_hives[n].hkey; n++) {
+ if(known_hives[n].hkey == hkey_type)
+ break;
+ }
+
+ if (known_hives[n].open == NULL) {
+ DEBUG(1, ("No such hive %d\n", hkey_type));
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ mykeydata = talloc_zero(ctx, struct rpc_key);
+ W_ERROR_HAVE_NO_MEMORY(mykeydata);
+ mykeydata->key.context = ctx;
+ mykeydata->binding_handle = rctx->binding_handle;
+ mykeydata->num_values = -1;
+ mykeydata->num_subkeys = -1;
+ *k = (struct registry_key *)mykeydata;
+ return known_hives[n].open(mykeydata->binding_handle, mykeydata, &mykeydata->pol);
+}
+
+#if 0
+static WERROR rpc_key_put_rpc_data(TALLOC_CTX *mem_ctx, struct registry_key *k)
+{
+ struct winreg_OpenKey r;
+ struct rpc_key_data *mykeydata;
+
+ k->backend_data = mykeydata = talloc_zero(mem_ctx, struct rpc_key_data);
+ mykeydata->num_values = -1;
+ mykeydata->num_subkeys = -1;
+
+ /* Then, open the handle using the hive */
+
+ ZERO_STRUCT(r);
+ r.in.handle = &(((struct rpc_key_data *)k->hive->root->backend_data)->pol);
+ r.in.keyname.name = k->path;
+ r.in.unknown = 0x00000000;
+ r.in.access_mask = 0x02000000;
+ r.out.handle = &mykeydata->pol;
+
+ dcerpc_winreg_OpenKey((struct dcerpc_pipe *)k->hive->backend_data,
+ mem_ctx, &r);
+
+ return r.out.result;
+}
+#endif
+
+static WERROR rpc_open_key(TALLOC_CTX *mem_ctx, struct registry_key *h,
+ const char *name, struct registry_key **key)
+{
+ struct rpc_key *parentkeydata = talloc_get_type(h, struct rpc_key),
+ *mykeydata;
+ struct winreg_OpenKey r;
+ NTSTATUS status;
+
+ mykeydata = talloc_zero(mem_ctx, struct rpc_key);
+ W_ERROR_HAVE_NO_MEMORY(mykeydata);
+ mykeydata->key.context = parentkeydata->key.context;
+ mykeydata->binding_handle = parentkeydata->binding_handle;
+ mykeydata->num_values = -1;
+ mykeydata->num_subkeys = -1;
+ *key = (struct registry_key *)mykeydata;
+
+ /* Then, open the handle using the hive */
+ ZERO_STRUCT(r);
+ r.in.parent_handle = &parentkeydata->pol;
+ r.in.keyname.name = name;
+ r.in.options = 0x00000000;
+ r.in.access_mask = 0x02000000;
+ r.out.handle = &mykeydata->pol;
+
+ status = dcerpc_winreg_OpenKey_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("OpenKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_value_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ uint32_t n,
+ const char **value_name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_EnumValue r;
+ struct winreg_ValNameBuf name;
+ uint8_t value;
+ uint32_t val_size = MAX_VALSIZE;
+ uint32_t zero = 0;
+ WERROR error;
+ NTSTATUS status;
+
+ if (mykeydata->num_values == -1) {
+ error = rpc_query_key(mem_ctx, parent);
+ if(!W_ERROR_IS_OK(error)) return error;
+ }
+
+ name.name = "";
+ name.size = MAX_NAMESIZE;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.enum_index = n;
+ r.in.name = &name;
+ r.in.type = (enum winreg_Type *) type;
+ r.in.value = &value;
+ r.in.size = &val_size;
+ r.in.length = &zero;
+ r.out.name = &name;
+ r.out.type = (enum winreg_Type *) type;
+ r.out.value = &value;
+ r.out.size = &val_size;
+ r.out.length = &zero;
+
+ status = dcerpc_winreg_EnumValue_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("EnumValue failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ *value_name = talloc_steal(mem_ctx, r.out.name->name);
+ *type = *(r.out.type);
+ *data = data_blob_talloc(mem_ctx, r.out.value, *r.out.length);
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_value_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ const char *value_name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_QueryValue r;
+ struct winreg_String name;
+ uint8_t value;
+ uint32_t val_size = MAX_VALSIZE;
+ uint32_t zero = 0;
+ WERROR error;
+ NTSTATUS status;
+
+ if (mykeydata->num_values == -1) {
+ error = rpc_query_key(mem_ctx, parent);
+ if(!W_ERROR_IS_OK(error)) return error;
+ }
+
+ name.name = value_name;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.value_name = &name;
+ r.in.type = (enum winreg_Type *) type;
+ r.in.data = &value;
+ r.in.data_size = &val_size;
+ r.in.data_length = &zero;
+ r.out.type = (enum winreg_Type *) type;
+ r.out.data = &value;
+ r.out.data_size = &val_size;
+ r.out.data_length = &zero;
+
+ status = dcerpc_winreg_QueryValue_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("QueryValue failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ *type = *(r.out.type);
+ *data = data_blob_talloc(mem_ctx, r.out.data, *r.out.data_length);
+
+ return r.out.result;
+}
+
+static WERROR rpc_set_value(struct registry_key *key, const char *value_name,
+ uint32_t type, const DATA_BLOB data)
+{
+ struct rpc_key *mykeydata = talloc_get_type(key, struct rpc_key);
+ struct winreg_SetValue r;
+ struct winreg_String name;
+ NTSTATUS status;
+
+ name = (struct winreg_String) { .name = value_name };
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.name = name;
+ r.in.type = (enum winreg_Type)type;
+ r.in.data = data.data;
+ r.in.size = data.length;
+
+ status = dcerpc_winreg_SetValue_r(mykeydata->binding_handle, key, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("SetValue failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ return r.out.result;
+}
+
+static WERROR rpc_del_value(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ const char *value_name)
+{
+ struct rpc_key *mykeydata = talloc_get_type(key, struct rpc_key);
+ struct winreg_DeleteValue r;
+ struct winreg_String name;
+ NTSTATUS status;
+
+ name = (struct winreg_String) { .name = value_name };
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.value = name;
+
+ status = dcerpc_winreg_DeleteValue_r(mykeydata->binding_handle,
+ mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("DeleteValue failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_subkey_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ uint32_t n,
+ const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time)
+{
+ struct winreg_EnumKey r;
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_StringBuf namebuf, classbuf;
+ NTTIME change_time = 0;
+ NTSTATUS status;
+
+ namebuf.name = "";
+ namebuf.size = MAX_NAMESIZE;
+ classbuf.name = NULL;
+ classbuf.size = 0;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.enum_index = n;
+ r.in.name = &namebuf;
+ r.in.keyclass = &classbuf;
+ r.in.last_changed_time = &change_time;
+ r.out.name = &namebuf;
+ r.out.keyclass = &classbuf;
+ r.out.last_changed_time = &change_time;
+
+ status = dcerpc_winreg_EnumKey_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("EnumKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ if (name != NULL)
+ *name = talloc_steal(mem_ctx, r.out.name->name);
+ if (keyclass != NULL)
+ *keyclass = talloc_steal(mem_ctx, r.out.keyclass->name);
+ if (last_changed_time != NULL)
+ *last_changed_time = *(r.out.last_changed_time);
+
+ return r.out.result;
+}
+
+static WERROR rpc_add_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent, const char *path,
+ const char *key_class,
+ struct security_descriptor *sec,
+ struct registry_key **key)
+{
+ struct winreg_CreateKey r;
+ struct rpc_key *parentkd = talloc_get_type(parent, struct rpc_key);
+ struct rpc_key *rpck = talloc_zero(mem_ctx, struct rpc_key);
+
+ NTSTATUS status;
+
+ if (rpck == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ rpck->key.context = parentkd->key.context;
+ rpck->binding_handle = parentkd->binding_handle;
+ rpck->num_values = -1;
+ rpck->num_subkeys = -1;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &parentkd->pol;
+ r.in.name.name = path;
+ r.in.keyclass.name = NULL;
+ r.in.options = 0;
+ r.in.access_mask = 0x02000000;
+ r.in.secdesc = NULL;
+ r.in.action_taken = NULL;
+ r.out.new_handle = &rpck->pol;
+ r.out.action_taken = NULL;
+
+ status = dcerpc_winreg_CreateKey_r(parentkd->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(rpck);
+ DEBUG(1, ("CreateKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ rpck->binding_handle = parentkd->binding_handle;
+ *key = (struct registry_key *)rpck;
+
+ return r.out.result;
+}
+
+static WERROR rpc_query_key(TALLOC_CTX *mem_ctx, const struct registry_key *k)
+{
+ struct winreg_QueryInfoKey r;
+ struct rpc_key *mykeydata = talloc_get_type(k, struct rpc_key);
+ struct winreg_String classname;
+ NTSTATUS status;
+
+ classname.name = NULL;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.classname = &classname;
+ r.out.classname = &classname;
+ r.out.num_subkeys = &mykeydata->num_subkeys;
+ r.out.max_subkeylen = &mykeydata->max_subkeylen;
+ r.out.max_classlen = &mykeydata->max_classlen;
+ r.out.num_values = &mykeydata->num_values;
+ r.out.max_valnamelen = &mykeydata->max_valnamelen;
+ r.out.max_valbufsize = &mykeydata->max_valbufsize;
+ r.out.secdescsize = &mykeydata->secdescsize;
+ r.out.last_changed_time = &mykeydata->last_changed_time;
+
+ status = dcerpc_winreg_QueryInfoKey_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("QueryInfoKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ mykeydata->classname = talloc_steal(mem_ctx, r.out.classname->name);
+
+ return r.out.result;
+}
+
+static WERROR rpc_del_key(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name)
+{
+ NTSTATUS status;
+ struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key);
+ struct winreg_DeleteKey r;
+
+ ZERO_STRUCT(r);
+ r.in.handle = &mykeydata->pol;
+ r.in.key.name = name;
+
+ status = dcerpc_winreg_DeleteKey_r(mykeydata->binding_handle, mem_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("DeleteKey failed - %s\n", nt_errstr(status)));
+ return ntstatus_to_werror(status);
+ }
+
+ return r.out.result;
+}
+
+static WERROR rpc_get_info(TALLOC_CTX *mem_ctx, const struct registry_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_changed_time,
+ uint32_t *max_subkeylen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ struct rpc_key *mykeydata = talloc_get_type(key, struct rpc_key);
+ WERROR error;
+
+ if (mykeydata->num_values == -1) {
+ error = rpc_query_key(mem_ctx, key);
+ if(!W_ERROR_IS_OK(error)) return error;
+ }
+
+ if (classname != NULL)
+ *classname = mykeydata->classname;
+
+ if (num_subkeys != NULL)
+ *num_subkeys = mykeydata->num_subkeys;
+
+ if (num_values != NULL)
+ *num_values = mykeydata->num_values;
+
+ if (last_changed_time != NULL)
+ *last_changed_time = mykeydata->last_changed_time;
+
+ if (max_subkeylen != NULL)
+ *max_subkeylen = mykeydata->max_subkeylen;
+
+ if (max_valnamelen != NULL)
+ *max_valnamelen = mykeydata->max_valnamelen;
+
+ if (max_valbufsize != NULL)
+ *max_valbufsize = mykeydata->max_valbufsize;
+
+ return WERR_OK;
+}
+
+static struct registry_operations reg_backend_rpc = {
+ .name = "rpc",
+ .open_key = rpc_open_key,
+ .get_predefined_key = rpc_get_predefined_key,
+ .enum_key = rpc_get_subkey_by_index,
+ .enum_value = rpc_get_value_by_index,
+ .get_value = rpc_get_value_by_name,
+ .set_value = rpc_set_value,
+ .delete_value = rpc_del_value,
+ .create_key = rpc_add_key,
+ .delete_key = rpc_del_key,
+ .get_key_info = rpc_get_info,
+};
+
+_PUBLIC_ WERROR reg_open_remote(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials,
+ struct loadparm_context *lp_ctx,
+ const char *location, struct tevent_context *ev)
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p;
+ struct rpc_registry_context *rctx;
+
+ dcerpc_init();
+
+ rctx = talloc(mem_ctx, struct rpc_registry_context);
+ W_ERROR_HAVE_NO_MEMORY(rctx);
+
+ /* Default to local smbd if no connection is specified */
+ if (!location) {
+ location = talloc_strdup(rctx, "ncalrpc:");
+ }
+
+ status = dcerpc_pipe_connect(rctx /* TALLOC_CTX */,
+ &p, location,
+ &ndr_table_winreg,
+ credentials, ev, lp_ctx);
+ if(NT_STATUS_IS_ERR(status)) {
+ DEBUG(1, ("Unable to open '%s': %s\n", location,
+ nt_errstr(status)));
+ talloc_free(rctx);
+ *ctx = NULL;
+ return ntstatus_to_werror(status);
+ }
+
+ rctx->pipe = p;
+ rctx->binding_handle = p->binding_handle;
+
+ *ctx = (struct registry_context *)rctx;
+ (*ctx)->ops = &reg_backend_rpc;
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/samba.c b/source4/lib/registry/samba.c
new file mode 100644
index 0000000..ff52297
--- /dev/null
+++ b/source4/lib/registry/samba.c
@@ -0,0 +1,100 @@
+/*
+ Unix SMB/CIFS implementation.
+ Copyright (C) Jelmer Vernooij 2004-2007.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "registry.h"
+#include "param/param.h"
+
+/**
+ * @file
+ * @brief Samba-specific registry functions
+ */
+
+static WERROR mount_samba_hive(struct registry_context *ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *auth_info,
+ struct cli_credentials *creds,
+ const char *name,
+ uint32_t hive_id)
+{
+ WERROR error;
+ struct hive_key *hive;
+ const char *location;
+
+ location = talloc_asprintf(ctx, "%s/%s.ldb",
+ lpcfg_private_dir(lp_ctx),
+ name);
+ W_ERROR_HAVE_NO_MEMORY(location);
+
+ error = reg_open_hive(ctx, location, auth_info, creds, event_ctx, lp_ctx, &hive);
+
+ if (W_ERROR_EQUAL(error, WERR_FILE_NOT_FOUND))
+ error = reg_open_ldb_file(ctx, location, auth_info,
+ creds, event_ctx, lp_ctx, &hive);
+
+ talloc_free(discard_const_p(char, location));
+
+ if (!W_ERROR_IS_OK(error))
+ return error;
+
+ return reg_mount_hive(ctx, hive, hive_id, NULL);
+}
+
+
+_PUBLIC_ WERROR reg_open_samba(TALLOC_CTX *mem_ctx,
+ struct registry_context **ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct auth_session_info *session_info,
+ struct cli_credentials *credentials)
+{
+ WERROR result;
+
+ result = reg_open_local(mem_ctx, ctx);
+ if (!W_ERROR_IS_OK(result)) {
+ return result;
+ }
+
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hklm", HKEY_LOCAL_MACHINE);
+
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hkcr", HKEY_CLASSES_ROOT);
+
+ /* FIXME: Should be mounted from NTUSER.DAT in the home directory of the
+ * current user */
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hkcu", HKEY_CURRENT_USER);
+
+ mount_samba_hive(*ctx, ev_ctx, lp_ctx, session_info, credentials,
+ "hku", HKEY_USERS);
+
+ /* FIXME: Different hive backend for HKEY_CLASSES_ROOT: merged view of HKEY_LOCAL_MACHINE\Software\Classes
+ * and HKEY_CURRENT_USER\Software\Classes */
+
+ /* FIXME: HKEY_CURRENT_CONFIG is an alias for HKEY_LOCAL_MACHINE\System\CurrentControlSet\Hardware Profiles\Current */
+
+ /* FIXME: HKEY_PERFORMANCE_DATA is dynamically generated */
+
+ /* FIXME: HKEY_LOCAL_MACHINE\Hardware is autogenerated */
+
+ /* FIXME: HKEY_LOCAL_MACHINE\Security\SAM is an alias for HKEY_LOCAL_MACHINE\SAM */
+
+ return WERR_OK;
+}
diff --git a/source4/lib/registry/tests/diff.c b/source4/lib/registry/tests/diff.c
new file mode 100644
index 0000000..5d2bfd7
--- /dev/null
+++ b/source4/lib/registry/tests/diff.c
@@ -0,0 +1,291 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry diff functionality
+
+ Copyright (C) Jelmer Vernooij 2007
+ Copyright (C) Wilco Baan Hofman 2008
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "param/param.h"
+#include "lib/registry/tests/proto.h"
+
+struct diff_tcase_data {
+ struct registry_context *r1_ctx;
+ struct registry_context *r2_ctx;
+ struct reg_diff_callbacks *callbacks;
+ void *callback_data;
+ char *tempdir;
+ char *filename;
+};
+
+static bool test_generate_diff(struct torture_context *tctx, void *tcase_data)
+{
+ WERROR error;
+ struct diff_tcase_data *td = tcase_data;
+
+ error = reg_generate_diff(td->r1_ctx, td->r2_ctx,
+ td->callbacks,
+ td->callback_data);
+ torture_assert_werr_ok(tctx, error, "reg_generate_diff");
+
+ return true;
+}
+
+#if 0
+static bool test_diff_load(struct torture_context *tctx, void *tcase_data)
+{
+ struct diff_tcase_data *td = tcase_data;
+ struct reg_diff_callbacks *callbacks;
+ void *data;
+ WERROR error;
+
+ error = reg_diff_load(td->filename, callbacks, data);
+ torture_assert_werr_ok(tctx, error, "reg_diff_load");
+
+ return true;
+}
+#endif
+static bool test_diff_apply(struct torture_context *tctx, void *tcase_data)
+{
+ struct diff_tcase_data *td = tcase_data;
+ struct registry_key *key;
+ WERROR error;
+
+ error = reg_diff_apply(td->r1_ctx, td->filename);
+ torture_assert_werr_ok(tctx, error, "reg_diff_apply");
+
+ error = td->r1_ctx->ops->get_predefined_key(td->r1_ctx, HKEY_LOCAL_MACHINE, &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKEY_LOCAL_MACHINE failed");
+
+ /* If this generates an error it could be that the apply doesn't work,
+ * but also that the reg_generate_diff didn't work. */
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Software", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\Software failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Microsoft", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\Software\\Microsoft failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Windows", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\Microsoft\\Windows failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "CurrentVersion", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\Windows\\CurrentVersion failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Policies", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\CurrentVersion\\Policies failed");
+ error = td->r1_ctx->ops->open_key(td->r1_ctx, key, "Explorer", &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKLM\\..\\Policies\\Explorer failed");
+
+ return true;
+}
+
+static const char *added_key = NULL;
+
+static WERROR test_add_key(void *callback_data, const char *key_name)
+{
+ added_key = talloc_strdup(callback_data, key_name);
+
+ return WERR_OK;
+}
+
+static bool test_generate_diff_key_add(struct torture_context *tctx, void *tcase_data)
+{
+ struct reg_diff_callbacks cb;
+ struct registry_key rk;
+
+ return true;
+
+ ZERO_STRUCT(cb);
+
+ cb.add_key = test_add_key;
+
+ if (W_ERROR_IS_OK(reg_generate_diff_key(&rk, NULL, "bla", &cb, tctx)))
+ return false;
+
+ torture_assert_str_equal(tctx, added_key, "bla", "key added");
+
+ return true;
+}
+
+static bool test_generate_diff_key_null(struct torture_context *tctx, void *tcase_data)
+{
+ struct reg_diff_callbacks cb;
+
+ ZERO_STRUCT(cb);
+
+ if (!W_ERROR_IS_OK(reg_generate_diff_key(NULL, NULL, "", &cb, NULL)))
+ return false;
+
+ return true;
+}
+
+static void tcase_add_tests (struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "test_generate_diff_key_add",
+ test_generate_diff_key_add);
+ torture_tcase_add_simple_test(tcase, "test_generate_diff_key_null",
+ test_generate_diff_key_null);
+ torture_tcase_add_simple_test(tcase, "test_generate_diff",
+ test_generate_diff);
+ torture_tcase_add_simple_test(tcase, "test_diff_apply",
+ test_diff_apply);
+/* torture_tcase_add_simple_test(tcase, "test_diff_load",
+ test_diff_load);
+*/
+}
+
+static bool diff_setup_tcase(struct torture_context *tctx, void **data)
+{
+ struct registry_context *r1_ctx, *r2_ctx;
+ WERROR error;
+ NTSTATUS status;
+ struct hive_key *r1_hklm, *r1_hkcu;
+ struct hive_key *r2_hklm, *r2_hkcu;
+ const char *filename;
+ struct diff_tcase_data *td;
+ struct registry_key *key, *newkey;
+ DATA_BLOB blob;
+
+ td = talloc(tctx, struct diff_tcase_data);
+
+ /* Create two registry contexts */
+ error = reg_open_local(tctx, &r1_ctx);
+ torture_assert_werr_ok(tctx, error, "Opening registry 1 for patch tests failed");
+
+ error = reg_open_local(tctx, &r2_ctx);
+ torture_assert_werr_ok(tctx, error, "Opening registry 2 for patch tests failed");
+
+ /* Create temp directory */
+ status = torture_temp_dir(tctx, "patchfile", &td->tempdir);
+ torture_assert_ntstatus_ok(tctx, status, "Creating temp dir failed");
+
+ /* Create and mount HKLM and HKCU hives for registry 1 */
+ filename = talloc_asprintf(tctx, "%s/r1_local_machine.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r1_hklm);
+ torture_assert_werr_ok(tctx, error, "Opening local machine file failed");
+
+ error = reg_mount_hive(r1_ctx, r1_hklm, HKEY_LOCAL_MACHINE, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ filename = talloc_asprintf(tctx, "%s/r1_current_user.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r1_hkcu);
+ torture_assert_werr_ok(tctx, error, "Opening current user file failed");
+
+ error = reg_mount_hive(r1_ctx, r1_hkcu, HKEY_CURRENT_USER, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ /* Create and mount HKLM and HKCU hives for registry 2 */
+ filename = talloc_asprintf(tctx, "%s/r2_local_machine.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r2_hklm);
+ torture_assert_werr_ok(tctx, error, "Opening local machine file failed");
+
+ error = reg_mount_hive(r2_ctx, r2_hklm, HKEY_LOCAL_MACHINE, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ filename = talloc_asprintf(tctx, "%s/r2_current_user.ldb", td->tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &r2_hkcu);
+ torture_assert_werr_ok(tctx, error, "Opening current user file failed");
+
+ error = reg_mount_hive(r2_ctx, r2_hkcu, HKEY_CURRENT_USER, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ error = r1_ctx->ops->get_predefined_key(r1_ctx, HKEY_CURRENT_USER, &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKEY_CURRENT_USER failed");
+ error = r1_ctx->ops->create_key(r1_ctx, key, "Network", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Opening HKCU\\Network failed");
+ error = r1_ctx->ops->create_key(r1_ctx, newkey, "L", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Opening HKCU\\Network\\L failed");
+
+ error = r2_ctx->ops->get_predefined_key(r2_ctx, HKEY_LOCAL_MACHINE, &key);
+ torture_assert_werr_ok(tctx, error, "Opening HKEY_LOCAL_MACHINE failed");
+ error = r2_ctx->ops->create_key(r2_ctx, key, "Software", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\Sofware failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Microsoft", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\Software\\Microsoft failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Windows", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\Software\\Microsoft\\Windows failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "CurrentVersion", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\..\\Windows\\CurrentVersion failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Policies", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\..\\CurrentVersion\\Policies failed");
+ error = r2_ctx->ops->create_key(r2_ctx, newkey, "Explorer", NULL, NULL, &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating HKLM\\..\\Policies\\Explorer failed");
+
+ blob.data = talloc_array(r2_ctx, uint8_t, 4);
+ /* set "0x03FFFFFF" in little endian format */
+ blob.data[0] = 0xFF; blob.data[1] = 0xFF;
+ blob.data[2] = 0xFF; blob.data[3] = 0x03;
+ blob.length = 4;
+
+ r1_ctx->ops->set_value(newkey, "NoDrives", REG_DWORD, blob);
+
+ /* Set test case data */
+ td->r1_ctx = r1_ctx;
+ td->r2_ctx = r2_ctx;
+
+ *data = td;
+
+ return true;
+}
+
+static bool diff_setup_preg_tcase (struct torture_context *tctx, void **data)
+{
+ struct diff_tcase_data *td;
+ WERROR error;
+
+ diff_setup_tcase(tctx, data);
+ td = *data;
+
+ td->filename = talloc_asprintf(tctx, "%s/test.pol", td->tempdir);
+ error = reg_preg_diff_save(tctx, td->filename, &td->callbacks,
+ &td->callback_data);
+ torture_assert_werr_ok(tctx, error, "reg_preg_diff_save");
+
+ return true;
+}
+
+static bool diff_setup_dotreg_tcase (struct torture_context *tctx, void **data)
+{
+ struct diff_tcase_data *td;
+ WERROR error;
+
+ diff_setup_tcase(tctx, data);
+ td = *data;
+
+ td->filename = talloc_asprintf(tctx, "%s/test.reg", td->tempdir);
+ error = reg_dotreg_diff_save(tctx, td->filename, &td->callbacks,
+ &td->callback_data);
+ torture_assert_werr_ok(tctx, error, "reg_dotreg_diff_save");
+
+ return true;
+}
+
+struct torture_suite *torture_registry_diff(TALLOC_CTX *mem_ctx)
+{
+ struct torture_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "diff");
+
+ tcase = torture_suite_add_tcase(suite, "PReg");
+ torture_tcase_set_fixture(tcase, diff_setup_preg_tcase, NULL);
+ tcase_add_tests(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "dotreg");
+ torture_tcase_set_fixture(tcase, diff_setup_dotreg_tcase, NULL);
+ tcase_add_tests(tcase);
+
+ return suite;
+}
diff --git a/source4/lib/registry/tests/generic.c b/source4/lib/registry/tests/generic.c
new file mode 100644
index 0000000..2ef6f84
--- /dev/null
+++ b/source4/lib/registry/tests/generic.c
@@ -0,0 +1,179 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry library
+
+ Copyright (C) Jelmer Vernooij 2005-2007
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "param/param.h"
+#include "lib/registry/tests/proto.h"
+
+static bool test_str_regtype(struct torture_context *ctx)
+{
+ torture_assert_str_equal(ctx, str_regtype(0),
+ "REG_NONE", "REG_NONE failed");
+ torture_assert_str_equal(ctx, str_regtype(1),
+ "REG_SZ", "REG_SZ failed");
+ torture_assert_str_equal(ctx, str_regtype(2),
+ "REG_EXPAND_SZ", "REG_EXPAND_SZ failed");
+ torture_assert_str_equal(ctx, str_regtype(3),
+ "REG_BINARY", "REG_BINARY failed");
+ torture_assert_str_equal(ctx, str_regtype(4),
+ "REG_DWORD", "REG_DWORD failed");
+ torture_assert_str_equal(ctx, str_regtype(5),
+ "REG_DWORD_BIG_ENDIAN", "REG_DWORD_BIG_ENDIAN failed");
+ torture_assert_str_equal(ctx, str_regtype(6),
+ "REG_LINK", "REG_LINK failed");
+ torture_assert_str_equal(ctx, str_regtype(7),
+ "REG_MULTI_SZ", "REG_MULTI_SZ failed");
+ torture_assert_str_equal(ctx, str_regtype(8),
+ "REG_RESOURCE_LIST", "REG_RESOURCE_LIST failed");
+ torture_assert_str_equal(ctx, str_regtype(9),
+ "REG_FULL_RESOURCE_DESCRIPTOR", "REG_FULL_RESOURCE_DESCRIPTOR failed");
+ torture_assert_str_equal(ctx, str_regtype(10),
+ "REG_RESOURCE_REQUIREMENTS_LIST", "REG_RESOURCE_REQUIREMENTS_LIST failed");
+ torture_assert_str_equal(ctx, str_regtype(11),
+ "REG_QWORD", "REG_QWORD failed");
+
+ return true;
+}
+
+
+static bool test_reg_val_data_string_dword(struct torture_context *ctx)
+{
+ uint8_t d[] = { 0x20, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+ torture_assert_str_equal(ctx, "0x00000020",
+ reg_val_data_string(ctx, REG_DWORD, db),
+ "dword failed");
+ return true;
+}
+
+static bool test_reg_val_data_string_dword_big_endian(struct torture_context *ctx)
+{
+ uint8_t d[] = { 0x20, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+ torture_assert_str_equal(ctx, "0x00000020",
+ reg_val_data_string(ctx, REG_DWORD_BIG_ENDIAN, db),
+ "dword failed");
+ return true;
+}
+
+static bool test_reg_val_data_string_qword(struct torture_context *ctx)
+{
+ uint8_t d[] = { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 8 };
+ torture_assert_str_equal(ctx, "0x0000000000000020",
+ reg_val_data_string(ctx, REG_QWORD, db),
+ "qword failed");
+ return true;
+}
+
+static bool test_reg_val_data_string_sz(struct torture_context *ctx)
+{
+ DATA_BLOB db;
+ convert_string_talloc(ctx, CH_UTF8, CH_UTF16,
+ "bla", 3, (void **)&db.data, &db.length);
+ torture_assert_str_equal(ctx, "bla",
+ reg_val_data_string(ctx, REG_SZ, db),
+ "sz failed");
+ db.length = 4;
+ torture_assert_str_equal(ctx, "bl",
+ reg_val_data_string(ctx, REG_SZ, db),
+ "sz failed");
+ return true;
+}
+
+static bool test_reg_val_data_string_binary(struct torture_context *ctx)
+{
+ uint8_t x[] = { 0x1, 0x2, 0x3, 0x4 };
+ DATA_BLOB db = { x, 4 };
+ torture_assert_str_equal(ctx, "01020304",
+ reg_val_data_string(ctx, REG_BINARY, db),
+ "binary failed");
+ return true;
+}
+
+
+static bool test_reg_val_data_string_empty(struct torture_context *ctx)
+{
+ DATA_BLOB db = { NULL, 0 };
+ torture_assert_str_equal(ctx, "",
+ reg_val_data_string(ctx, REG_BINARY, db),
+ "empty failed");
+ return true;
+}
+
+static bool test_reg_val_description(struct torture_context *ctx)
+{
+ DATA_BLOB data;
+ convert_string_talloc(ctx, CH_UTF8, CH_UTF16,
+ "stationary traveller",
+ strlen("stationary traveller"),
+ (void **)&data.data, &data.length);
+ torture_assert_str_equal(ctx, "camel = REG_SZ : stationary traveller",
+ reg_val_description(ctx, "camel", REG_SZ, data),
+ "reg_val_description failed");
+ return true;
+}
+
+
+static bool test_reg_val_description_nullname(struct torture_context *ctx)
+{
+ DATA_BLOB data;
+ convert_string_talloc(ctx, CH_UTF8, CH_UTF16,
+ "west berlin",
+ strlen("west berlin"),
+ (void **)&data.data, &data.length);
+ torture_assert_str_equal(ctx, "<No Name> = REG_SZ : west berlin",
+ reg_val_description(ctx, NULL, REG_SZ, data),
+ "description with null name failed");
+ return true;
+}
+
+struct torture_suite *torture_registry(TALLOC_CTX *mem_ctx)
+{
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "registry");
+ torture_suite_add_simple_test(suite, "str_regtype",
+ test_str_regtype);
+ torture_suite_add_simple_test(suite, "reg_val_data_string dword",
+ test_reg_val_data_string_dword);
+ torture_suite_add_simple_test(suite, "reg_val_data_string dword_big_endian",
+ test_reg_val_data_string_dword_big_endian);
+ torture_suite_add_simple_test(suite, "reg_val_data_string qword",
+ test_reg_val_data_string_qword);
+ torture_suite_add_simple_test(suite, "reg_val_data_string sz",
+ test_reg_val_data_string_sz);
+ torture_suite_add_simple_test(suite, "reg_val_data_string binary",
+ test_reg_val_data_string_binary);
+ torture_suite_add_simple_test(suite, "reg_val_data_string empty",
+ test_reg_val_data_string_empty);
+ torture_suite_add_simple_test(suite, "reg_val_description",
+ test_reg_val_description);
+ torture_suite_add_simple_test(suite, "reg_val_description null",
+ test_reg_val_description_nullname);
+
+ torture_suite_add_suite(suite, torture_registry_hive(suite));
+ torture_suite_add_suite(suite, torture_registry_registry(suite));
+ torture_suite_add_suite(suite, torture_registry_diff(suite));
+
+ return suite;
+}
diff --git a/source4/lib/registry/tests/hive.c b/source4/lib/registry/tests/hive.c
new file mode 100644
index 0000000..aca5cff
--- /dev/null
+++ b/source4/lib/registry/tests/hive.c
@@ -0,0 +1,440 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry library - hives
+
+ Copyright (C) Jelmer Vernooij 2005-2007
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "system/filesys.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+#include "lib/registry/tests/proto.h"
+
+static bool test_del_nonexistent_key(struct torture_context *tctx,
+ const void *test_data)
+{
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ WERROR error = hive_key_del(tctx, root, "bla");
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "invalid return code");
+
+ return true;
+}
+
+static bool test_keyinfo_root(struct torture_context *tctx,
+ const void *test_data)
+{
+ uint32_t num_subkeys, num_values;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ WERROR error;
+
+ /* This is a new backend. There should be no subkeys and no
+ * values */
+ error = hive_key_get_info(tctx, root, NULL, &num_subkeys, &num_values,
+ NULL, NULL, NULL, NULL);
+ torture_assert_werr_ok(tctx, error, "reg_key_num_subkeys()");
+
+ torture_assert_int_equal(tctx, num_subkeys, 0,
+ "New key has non-zero subkey count");
+
+ torture_assert_werr_ok(tctx, error, "reg_key_num_values");
+
+ torture_assert_int_equal(tctx, num_values, 0,
+ "New key has non-zero value count");
+
+ return true;
+}
+
+static bool test_keyinfo_nums(struct torture_context *tctx, void *test_data)
+{
+ uint32_t num_subkeys, num_values;
+ struct hive_key *root = (struct hive_key *)test_data;
+ WERROR error;
+ struct hive_key *subkey;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+
+ error = hive_key_add_name(tctx, root, "Nested Keyll", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(root, "Answer", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ /* This is a new backend. There should be no subkeys and no
+ * values */
+ error = hive_key_get_info(tctx, root, NULL, &num_subkeys, &num_values,
+ NULL, NULL, NULL, NULL);
+ torture_assert_werr_ok(tctx, error, "reg_key_num_subkeys()");
+
+ torture_assert_int_equal(tctx, num_subkeys, 1, "subkey count");
+
+ torture_assert_werr_ok(tctx, error, "reg_key_num_values");
+
+ torture_assert_int_equal(tctx, num_values, 1, "value count");
+
+ return true;
+}
+
+static bool test_add_subkey(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ error = hive_key_add_name(mem_ctx, root, "Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_del(mem_ctx, root, "Nested Key");
+ torture_assert_werr_ok(tctx, error, "reg_key_del");
+
+ return true;
+}
+
+static bool test_del_recursive(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ struct hive_key *subkey2;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+
+ /* Create a new key under the root */
+ error = hive_key_add_name(mem_ctx, root, "Parent Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ /* Create a new key under "Parent Key" */
+ error = hive_key_add_name(mem_ctx, subkey, "Child Key", NULL,
+ NULL, &subkey2);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ /* Create a new value under "Child Key" */
+ error = hive_key_set_value(subkey2, "Answer Recursive", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ /* Deleting "Parent Key" will also delete "Child Key" and the value. */
+ error = hive_key_del(mem_ctx, root, "Parent Key");
+ torture_assert_werr_ok(tctx, error, "hive_key_del");
+
+ return true;
+}
+
+static bool test_flush_key(struct torture_context *tctx, void *test_data)
+{
+ struct hive_key *root = (struct hive_key *)test_data;
+
+ torture_assert_werr_ok(tctx, hive_key_flush(root), "flush key");
+
+ return true;
+}
+
+static bool test_del_key(struct torture_context *tctx, const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+
+ error = hive_key_add_name(mem_ctx, root, "Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_del(mem_ctx, root, "Nested Key");
+ torture_assert_werr_ok(tctx, error, "reg_key_del");
+
+ error = hive_key_del(mem_ctx, root, "Nested Key");
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND, "reg_key_del");
+
+ return true;
+}
+
+static bool test_set_value(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+
+ error = hive_key_add_name(mem_ctx, root, "YA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ return true;
+}
+
+static bool test_get_value(struct torture_context *tctx, const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ uint32_t type;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 }, data;
+
+ error = hive_key_add_name(mem_ctx, root, "EYA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_get_value(mem_ctx, subkey, "Answer", &type, &data);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting missing value");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ error = hive_get_value(mem_ctx, subkey, "Answer", &type, &data);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_int_equal(tctx, data.length, 4, "value length");
+ torture_assert_int_equal(tctx, type, REG_DWORD, "value type");
+
+ torture_assert_mem_equal(tctx, data.data, db.data, 4, "value data");
+
+ return true;
+}
+
+static bool test_del_value(struct torture_context *tctx, const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ uint32_t type;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 };
+
+ error = hive_key_add_name(mem_ctx, root, "EEYA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ error = hive_key_del_value(mem_ctx, subkey, "Answer");
+ torture_assert_werr_ok(tctx, error, "deleting value");
+
+ error = hive_get_value(mem_ctx, subkey, "Answer", &type, &db);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND, "getting value");
+
+ error = hive_key_del_value(mem_ctx, subkey, "Answer");
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "deleting value");
+
+ return true;
+}
+
+static bool test_list_values(struct torture_context *tctx,
+ const void *test_data)
+{
+ WERROR error;
+ struct hive_key *subkey;
+ const struct hive_key *root = (const struct hive_key *)test_data;
+ TALLOC_CTX *mem_ctx = tctx;
+ uint32_t type;
+ uint8_t d[] = { 0x42, 0x00, 0x00, 0x00 };
+ DATA_BLOB db = { d, 4 }, data;
+ const char *name;
+
+ error = hive_key_add_name(mem_ctx, root, "AYAYA Nested Key", NULL,
+ NULL, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_key_set_value(subkey, "Answer", REG_DWORD, db);
+ torture_assert_werr_ok(tctx, error, "hive_key_set_value");
+
+ error = hive_get_value_by_index(mem_ctx, subkey, 0, &name,
+ &type, &data);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_str_equal(tctx, name, "Answer", "value name");
+
+ torture_assert_int_equal(tctx, data.length, 4, "value length");
+ torture_assert_int_equal(tctx, type, REG_DWORD, "value type");
+
+ torture_assert_mem_equal(tctx, data.data, db.data, 4, "value data");
+
+ error = hive_get_value_by_index(mem_ctx, subkey, 1, &name,
+ &type, &data);
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "getting missing value");
+
+ return true;
+}
+
+static bool test_hive_security(struct torture_context *tctx, const void *_data)
+{
+ struct hive_key *subkey = NULL;
+ const struct hive_key *root = _data;
+ WERROR error;
+ struct security_descriptor *osd, *nsd;
+
+ osd = security_descriptor_dacl_create(tctx,
+ 0,
+ NULL, NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+
+
+ error = hive_key_add_name(tctx, root, "SecurityKey", NULL,
+ osd, &subkey);
+ torture_assert_werr_ok(tctx, error, "hive_key_add_name");
+
+ error = hive_get_sec_desc(tctx, subkey, &nsd);
+ torture_assert_werr_ok (tctx, error, "getting security descriptor");
+
+ torture_assert(tctx, security_descriptor_equal(osd, nsd),
+ "security descriptor changed!");
+
+ /* Create a fresh security descriptor */
+ talloc_free(osd);
+ osd = security_descriptor_dacl_create(tctx,
+ 0,
+ NULL, NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+
+ error = hive_set_sec_desc(subkey, osd);
+ torture_assert_werr_ok(tctx, error, "setting security descriptor");
+
+ error = hive_get_sec_desc(tctx, subkey, &nsd);
+ torture_assert_werr_ok (tctx, error, "getting security descriptor");
+
+ torture_assert(tctx, security_descriptor_equal(osd, nsd),
+ "security descriptor changed!");
+
+ return true;
+}
+
+static void tcase_add_tests(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test_const(tcase, "del_nonexistent_key",
+ test_del_nonexistent_key);
+ torture_tcase_add_simple_test_const(tcase, "add_subkey",
+ test_add_subkey);
+ torture_tcase_add_simple_test(tcase, "flush_key",
+ test_flush_key);
+ /* test_del_recursive() test must run before test_keyinfo_root().
+ test_keyinfo_root() checks the number of subkeys, which verifies
+ the recursive delete worked properly. */
+ torture_tcase_add_simple_test_const(tcase, "del_recursive",
+ test_del_recursive);
+ torture_tcase_add_simple_test_const(tcase, "get_info",
+ test_keyinfo_root);
+ torture_tcase_add_simple_test(tcase, "get_info_nums",
+ test_keyinfo_nums);
+ torture_tcase_add_simple_test_const(tcase, "set_value",
+ test_set_value);
+ torture_tcase_add_simple_test_const(tcase, "get_value",
+ test_get_value);
+ torture_tcase_add_simple_test_const(tcase, "list_values",
+ test_list_values);
+ torture_tcase_add_simple_test_const(tcase, "del_key",
+ test_del_key);
+ torture_tcase_add_simple_test_const(tcase, "del_value",
+ test_del_value);
+ torture_tcase_add_simple_test_const(tcase, "check hive security",
+ test_hive_security);
+}
+
+static bool hive_setup_ldb(struct torture_context *tctx, void **data)
+{
+ struct hive_key *key;
+ WERROR error;
+ char *dirname;
+ NTSTATUS status;
+
+ status = torture_temp_dir(tctx, "hive-ldb", &dirname);
+ if (!NT_STATUS_IS_OK(status))
+ return false;
+
+ rmdir(dirname);
+
+ error = reg_open_ldb_file(tctx, dirname, NULL, NULL, tctx->ev, tctx->lp_ctx, &key);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to initialize ldb hive\n");
+ return false;
+ }
+
+ *data = key;
+
+ return true;
+}
+
+static bool hive_setup_regf(struct torture_context *tctx, void **data)
+{
+ struct hive_key *key;
+ WERROR error;
+ char *dirname;
+ NTSTATUS status;
+
+ status = torture_temp_dir(tctx, "hive-regf", &dirname);
+ if (!NT_STATUS_IS_OK(status))
+ return false;
+
+ rmdir(dirname);
+
+ error = reg_create_regf_file(tctx, dirname, 5, &key);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to create new regf file\n");
+ return false;
+ }
+
+ *data = key;
+
+ return true;
+}
+
+struct torture_suite *torture_registry_hive(TALLOC_CTX *mem_ctx)
+{
+ struct torture_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "hive");
+
+ tcase = torture_suite_add_tcase(suite, "ldb");
+ torture_tcase_set_fixture(tcase, hive_setup_ldb, NULL);
+ tcase_add_tests(tcase);
+
+ tcase = torture_suite_add_tcase(suite, "regf");
+ torture_tcase_set_fixture(tcase, hive_setup_regf, NULL);
+ tcase_add_tests(tcase);
+
+ return suite;
+}
diff --git a/source4/lib/registry/tests/registry.c b/source4/lib/registry/tests/registry.c
new file mode 100644
index 0000000..4d94b5a
--- /dev/null
+++ b/source4/lib/registry/tests/registry.c
@@ -0,0 +1,645 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ local testing of registry library - registry backend
+
+ Copyright (C) Jelmer Vernooij 2005-2007
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "torture/torture.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "libcli/security/security.h"
+#include "system/filesys.h"
+#include "lib/registry/tests/proto.h"
+
+/**
+ * Test obtaining a predefined key.
+ */
+static bool test_get_predefined(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+ return true;
+}
+
+/**
+ * Test obtaining a predefined key.
+ */
+static bool test_get_predefined_unknown(struct torture_context *tctx,
+ void *_data)
+{
+ struct registry_context *rctx = _data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, 1337, &root);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting predefined key failed");
+ return true;
+}
+
+static bool test_predef_key_by_name(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key_by_name(rctx, "HKEY_CLASSES_ROOT",
+ &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_get_predefined_key_by_name(rctx, "HKEY_classes_ROOT",
+ &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key case insensitively failed");
+
+ return true;
+}
+
+static bool test_predef_key_by_name_invalid(struct torture_context *tctx,
+ void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_get_predefined_key_by_name(rctx, "BLA", &root);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting predefined key failed");
+ return true;
+}
+
+/**
+ * Test creating a new subkey
+ */
+static bool test_create_subkey(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *newkey;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, root, "Bad Bentheim", NULL, NULL,
+ &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+ torture_assert(tctx, newkey != NULL, "Creating new key");
+
+ return true;
+}
+
+/**
+ * Test creating a new nested subkey
+ */
+static bool test_create_nested_subkey(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *newkey;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, root, "Hamburg\\Hamburg", NULL, NULL,
+ &newkey);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+ torture_assert(tctx, newkey != NULL, "Creating new key");
+
+ return true;
+}
+
+/**
+ * Test creating a new subkey
+ */
+static bool test_key_add_abs_top(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root;
+ WERROR error;
+
+ error = reg_key_add_abs(tctx, rctx, "HKEY_CLASSES_ROOT", 0, NULL,
+ &root);
+ torture_assert_werr_equal(tctx, error, WERR_ALREADY_EXISTS,
+ "create top level");
+
+ return true;
+}
+
+/**
+ * Test creating a new subkey
+ */
+static bool test_key_add_abs(struct torture_context *tctx, void *_data)
+{
+ WERROR error;
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *result1, *result2;
+
+ error = reg_key_add_abs(tctx, rctx, "HKEY_CLASSES_ROOT\\bloe", 0, NULL,
+ &result1);
+ torture_assert_werr_ok(tctx, error, "create lowest");
+
+ error = reg_key_add_abs(tctx, rctx, "HKEY_CLASSES_ROOT\\bloe\\bla", 0,
+ NULL, &result1);
+ torture_assert_werr_ok(tctx, error, "create nested");
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_open_key(tctx, root, "bloe", &result2);
+ torture_assert_werr_ok(tctx, error, "opening key");
+
+ error = reg_open_key(tctx, root, "bloe\\bla", &result2);
+ torture_assert_werr_ok(tctx, error, "opening key");
+
+ return true;
+}
+
+
+static bool test_del_key(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *newkey;
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, &root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, root, "Polen", NULL, NULL, &newkey);
+
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+ torture_assert(tctx, newkey != NULL, "Creating new key");
+
+ error = reg_key_del(tctx, root, "Polen");
+ torture_assert_werr_ok(tctx, error, "Delete key");
+
+ error = reg_key_del(tctx, root, "Polen");
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "Delete missing key");
+
+ return true;
+}
+
+/**
+ * Convenience function for opening the HKEY_CLASSES_ROOT hive and
+ * creating a single key for testing purposes.
+ */
+static bool create_test_key(struct torture_context *tctx,
+ struct registry_context *rctx,
+ const char *name,
+ struct registry_key **root,
+ struct registry_key **subkey)
+{
+ WERROR error;
+
+ error = reg_get_predefined_key(rctx, HKEY_CLASSES_ROOT, root);
+ torture_assert_werr_ok(tctx, error,
+ "getting predefined key failed");
+
+ error = reg_key_add_name(rctx, *root, name, NULL, NULL, subkey);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+
+ return true;
+}
+
+
+static bool test_flush_key(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *subkey;
+ WERROR error;
+
+ if (!create_test_key(tctx, rctx, "Bremen", &root, &subkey))
+ return false;
+
+ error = reg_key_flush(subkey);
+ torture_assert_werr_ok(tctx, error, "flush key");
+
+ torture_assert_werr_equal(tctx, reg_key_flush(NULL),
+ WERR_INVALID_PARAMETER, "flush key");
+
+ return true;
+}
+
+static bool test_query_key(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *subkey;
+ WERROR error;
+ NTTIME last_changed_time;
+ uint32_t num_subkeys, num_values;
+ const char *classname;
+ const char *data = "temp";
+
+ if (!create_test_key(tctx, rctx, "Muenchen", &root, &subkey))
+ return false;
+
+ error = reg_key_get_info(tctx, subkey, &classname,
+ &num_subkeys, &num_values,
+ &last_changed_time, NULL, NULL, NULL);
+
+ torture_assert_werr_ok(tctx, error, "get info key");
+ torture_assert(tctx, classname == NULL, "classname");
+ torture_assert_int_equal(tctx, num_subkeys, 0, "num subkeys");
+ torture_assert_int_equal(tctx, num_values, 0, "num values");
+
+ error = reg_val_set(subkey, "", REG_SZ,
+ data_blob_string_const(data));
+ torture_assert_werr_ok(tctx, error, "set default value");
+
+ error = reg_key_get_info(tctx, subkey, &classname,
+ &num_subkeys, &num_values,
+ &last_changed_time, NULL, NULL, NULL);
+
+ torture_assert_werr_ok(tctx, error, "get info key");
+ torture_assert(tctx, classname == NULL, "classname");
+ torture_assert_int_equal(tctx, num_subkeys, 0, "num subkeys");
+ torture_assert_int_equal(tctx, num_values, 1, "num values");
+
+ return true;
+}
+
+static bool test_query_key_nums(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *root, *subkey1, *subkey2;
+ WERROR error;
+ uint32_t num_subkeys, num_values;
+ char data[4];
+ SIVAL(data, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Berlin", &root, &subkey1))
+ return false;
+
+ error = reg_key_add_name(rctx, subkey1, "Bentheim", NULL, NULL,
+ &subkey2);
+ torture_assert_werr_ok(tctx, error, "Creating key return code");
+
+ error = reg_val_set(subkey1, "Answer", REG_DWORD,
+ data_blob_talloc(tctx, &data, sizeof(data)));
+ torture_assert_werr_ok(tctx, error, "set value");
+
+ error = reg_key_get_info(tctx, subkey1, NULL, &num_subkeys,
+ &num_values, NULL, NULL, NULL, NULL);
+
+ torture_assert_werr_ok(tctx, error, "get info key");
+ torture_assert_int_equal(tctx, num_subkeys, 1, "num subkeys");
+ torture_assert_int_equal(tctx, num_values, 1, "num values");
+
+ return true;
+}
+
+/**
+ * Test that the subkeys of a key can be enumerated, that
+ * the returned parameters for get_subkey_by_index are optional and
+ * that enumerating the parents of a non-top-level node works.
+ */
+static bool test_list_subkeys(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ NTTIME last_mod_time;
+ const char *classname, *name;
+
+ if (!create_test_key(tctx, rctx, "Goettingen", &root, &subkey))
+ return false;
+
+ error = reg_key_get_subkey_by_index(tctx, root, 0, &name, &classname,
+ &last_mod_time);
+
+ torture_assert_werr_ok(tctx, error, "Enum keys return code");
+ torture_assert_str_equal(tctx, name, "Goettingen", "Enum keys data");
+
+
+ error = reg_key_get_subkey_by_index(tctx, root, 0, NULL, NULL, NULL);
+
+ torture_assert_werr_ok(tctx, error,
+ "Enum keys with NULL arguments return code");
+
+ error = reg_key_get_subkey_by_index(tctx, root, 1, NULL, NULL, NULL);
+
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "Invalid error for no more items");
+
+ error = reg_key_get_subkey_by_index(tctx, subkey, 0, NULL, NULL, NULL);
+
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "Invalid error for no more items");
+
+ return true;
+}
+
+/**
+ * Test setting a value
+ */
+static bool test_set_value(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ char data[4];
+
+ SIVAL(data, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Dusseldorf", &root, &subkey))
+ return false;
+
+ error = reg_val_set(subkey, "Answer", REG_DWORD,
+ data_blob_talloc(tctx, data, sizeof(data)));
+ torture_assert_werr_ok (tctx, error, "setting value");
+
+ return true;
+}
+
+/**
+ * Test getting/setting security descriptors
+ */
+static bool test_security(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ struct security_descriptor *osd, *nsd;
+
+ if (!create_test_key(tctx, rctx, "Düsseldorf", &root, &subkey))
+ return false;
+
+ osd = security_descriptor_dacl_create(tctx,
+ 0,
+ NULL, NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_GENERIC_ALL,
+ SEC_ACE_FLAG_OBJECT_INHERIT,
+ NULL);
+
+ error = reg_set_sec_desc(subkey, osd);
+ torture_assert_werr_ok(tctx, error, "setting security descriptor");
+
+ error = reg_get_sec_desc(tctx, subkey, &nsd);
+ torture_assert_werr_ok (tctx, error, "getting security descriptor");
+
+ torture_assert(tctx, security_descriptor_equal(osd, nsd),
+ "security descriptor changed!");
+
+ return true;
+}
+
+/**
+ * Test getting a value
+ */
+static bool test_get_value(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ DATA_BLOB data;
+ char value[4];
+ uint32_t type;
+ const char *data_val = "temp";
+
+ SIVAL(value, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Duisburg", &root, &subkey))
+ return false;
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__, &type,
+ &data);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting missing value");
+
+ error = reg_val_set(subkey, __FUNCTION__, REG_DWORD,
+ data_blob_talloc(tctx, value, sizeof(value)));
+ torture_assert_werr_ok(tctx, error, "setting value");
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__, &type,
+ &data);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_int_equal(tctx, sizeof(value), data.length, "value length ok");
+ torture_assert_mem_equal(tctx, data.data, value, sizeof(value),
+ "value content ok");
+ torture_assert_int_equal(tctx, REG_DWORD, type, "value type");
+
+ error = reg_val_set(subkey, "", REG_SZ,
+ data_blob_talloc(tctx, data_val,
+ strlen(data_val)));
+ torture_assert_werr_ok(tctx, error, "set default value");
+
+ error = reg_key_get_value_by_name(tctx, subkey, "", &type,
+ &data);
+ torture_assert_werr_ok(tctx, error, "getting default value");
+ torture_assert_int_equal(tctx, REG_SZ, type, "value type ok");
+ torture_assert_int_equal(tctx, strlen(data_val), data.length, "value length ok");
+ torture_assert_str_equal(tctx, data_val, (char *)data.data, "value ok");
+
+ return true;
+}
+
+/**
+ * Test unsetting a value
+ */
+static bool test_del_value(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx =(struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ DATA_BLOB data;
+ uint32_t type;
+ char value[4];
+ const char *data_val = "temp";
+
+ SIVAL(value, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Warschau", &root, &subkey))
+ return false;
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__, &type,
+ &data);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting missing value");
+
+ error = reg_val_set(subkey, __FUNCTION__, REG_DWORD,
+ data_blob_talloc(tctx, value, sizeof(value)));
+ torture_assert_werr_ok (tctx, error, "setting value");
+
+ error = reg_del_value(tctx, subkey, __FUNCTION__);
+ torture_assert_werr_ok (tctx, error, "unsetting value");
+
+ error = reg_key_get_value_by_name(tctx, subkey, __FUNCTION__,
+ &type, &data);
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "getting missing value");
+
+ error = reg_del_value(tctx, subkey, "");
+ torture_assert_werr_equal(tctx, error, WERR_FILE_NOT_FOUND,
+ "unsetting missing default value");
+
+ error = reg_val_set(subkey, "", REG_SZ,
+ data_blob_talloc(tctx, data_val,
+ strlen(data_val)));
+ torture_assert_werr_ok(tctx, error, "set default value");
+
+ error = reg_del_value(tctx, subkey, "");
+ torture_assert_werr_ok (tctx, error, "unsetting default value");
+
+ return true;
+}
+
+/**
+ * Test listing values
+ */
+static bool test_list_values(struct torture_context *tctx, void *_data)
+{
+ struct registry_context *rctx = (struct registry_context *)_data;
+ struct registry_key *subkey = NULL, *root;
+ WERROR error;
+ DATA_BLOB data;
+ uint32_t type;
+ const char *name;
+ char value[4];
+ const char *data_val = "temp";
+
+ SIVAL(value, 0, 42);
+
+ if (!create_test_key(tctx, rctx, "Bonn", &root, &subkey))
+ return false;
+
+ error = reg_val_set(subkey, "bar", REG_DWORD,
+ data_blob_talloc(tctx, value, sizeof(value)));
+ torture_assert_werr_ok (tctx, error, "setting value");
+
+ error = reg_key_get_value_by_index(tctx, subkey, 0, &name,
+ &type, &data);
+ torture_assert_werr_ok(tctx, error, "getting value");
+
+ torture_assert_str_equal(tctx, name, "bar", "value name");
+ torture_assert_int_equal(tctx, sizeof(value), data.length, "value length");
+ torture_assert_mem_equal(tctx, data.data, value, sizeof(value),
+ "value content");
+ torture_assert_int_equal(tctx, REG_DWORD, type, "value type");
+
+ error = reg_key_get_value_by_index(tctx, subkey, 1, &name,
+ &type, &data);
+ torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS,
+ "getting missing value");
+
+ error = reg_val_set(subkey, "", REG_SZ,
+ data_blob_talloc(tctx, data_val, strlen(data_val)));
+ torture_assert_werr_ok(tctx, error, "set default value");
+
+ error = reg_key_get_value_by_index(tctx, subkey, 0, &name,
+ &type, &data);
+ torture_assert_werr_ok(tctx, error, "getting default value");
+ torture_assert_int_equal(tctx, REG_SZ, type, "value type ok");
+ torture_assert_int_equal(tctx, strlen(data_val), data.length, "value length ok");
+ torture_assert_str_equal(tctx, data_val, (char *)data.data, "value ok");
+
+ return true;
+}
+
+static bool setup_local_registry(struct torture_context *tctx, void **data)
+{
+ struct registry_context *rctx;
+ WERROR error;
+ char *tempdir;
+ NTSTATUS status;
+ struct hive_key *hive_key;
+ const char *filename;
+
+ error = reg_open_local(tctx, &rctx);
+ torture_assert_werr_ok(tctx, error, "Opening local registry failed");
+
+ status = torture_temp_dir(tctx, "registry-local", &tempdir);
+ torture_assert_ntstatus_ok(tctx, status, "Creating temp dir failed");
+
+ filename = talloc_asprintf(tctx, "%s/classes_root.ldb", tempdir);
+ error = reg_open_ldb_file(tctx, filename, NULL, NULL, tctx->ev, tctx->lp_ctx, &hive_key);
+ torture_assert_werr_ok(tctx, error, "Opening classes_root file failed");
+
+ error = reg_mount_hive(rctx, hive_key, HKEY_CLASSES_ROOT, NULL);
+ torture_assert_werr_ok(tctx, error, "Mounting hive failed");
+
+ *data = rctx;
+
+ return true;
+}
+
+static void tcase_add_tests(struct torture_tcase *tcase)
+{
+ torture_tcase_add_simple_test(tcase, "list_subkeys",
+ test_list_subkeys);
+ torture_tcase_add_simple_test(tcase, "get_predefined_key",
+ test_get_predefined);
+ torture_tcase_add_simple_test(tcase, "get_predefined_key",
+ test_get_predefined_unknown);
+ torture_tcase_add_simple_test(tcase, "create_key",
+ test_create_subkey);
+ torture_tcase_add_simple_test(tcase, "create_key",
+ test_create_nested_subkey);
+ torture_tcase_add_simple_test(tcase, "key_add_abs",
+ test_key_add_abs);
+ torture_tcase_add_simple_test(tcase, "key_add_abs_top",
+ test_key_add_abs_top);
+ torture_tcase_add_simple_test(tcase, "set_value",
+ test_set_value);
+ torture_tcase_add_simple_test(tcase, "get_value",
+ test_get_value);
+ torture_tcase_add_simple_test(tcase, "list_values",
+ test_list_values);
+ torture_tcase_add_simple_test(tcase, "del_key",
+ test_del_key);
+ torture_tcase_add_simple_test(tcase, "del_value",
+ test_del_value);
+ torture_tcase_add_simple_test(tcase, "flush_key",
+ test_flush_key);
+ torture_tcase_add_simple_test(tcase, "query_key",
+ test_query_key);
+ torture_tcase_add_simple_test(tcase, "query_key_nums",
+ test_query_key_nums);
+ torture_tcase_add_simple_test(tcase, "test_predef_key_by_name",
+ test_predef_key_by_name);
+ torture_tcase_add_simple_test(tcase, "security",
+ test_security);
+ torture_tcase_add_simple_test(tcase,"test_predef_key_by_name_invalid",
+ test_predef_key_by_name_invalid);
+}
+
+struct torture_suite *torture_registry_registry(TALLOC_CTX *mem_ctx)
+{
+ struct torture_tcase *tcase;
+ struct torture_suite *suite = torture_suite_create(mem_ctx, "registry");
+
+ tcase = torture_suite_add_tcase(suite, "local");
+ torture_tcase_set_fixture(tcase, setup_local_registry, NULL);
+ tcase_add_tests(tcase);
+
+ return suite;
+}
diff --git a/source4/lib/registry/tools/common.c b/source4/lib/registry/tools/common.c
new file mode 100644
index 0000000..a2fda8d
--- /dev/null
+++ b/source4/lib/registry/tools/common.c
@@ -0,0 +1,88 @@
+/*
+ Unix SMB/CIFS implementation.
+ Popt routines specifically for registry
+
+ Copyright (C) Jelmer Vernooij 2007
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "auth/credentials/credentials.h"
+#include "lib/registry/registry.h"
+#include "lib/registry/tools/common.h"
+
+struct registry_context *reg_common_open_remote(const char *remote,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *creds)
+{
+ struct registry_context *h = NULL;
+ WERROR error;
+
+ error = reg_open_remote(NULL, &h, NULL, creds, lp_ctx, remote, ev_ctx);
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to open remote registry at %s:%s \n",
+ remote, win_errstr(error));
+ return NULL;
+ }
+
+ return h;
+}
+
+struct registry_key *reg_common_open_file(const char *path,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct cli_credentials *creds)
+{
+ struct hive_key *hive_root;
+ struct registry_context *h = NULL;
+ WERROR error;
+
+ error = reg_open_hive(ev_ctx, path, NULL, creds, ev_ctx, lp_ctx, &hive_root);
+
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to open '%s': %s \n",
+ path, win_errstr(error));
+ return NULL;
+ }
+
+ error = reg_open_local(NULL, &h);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to initialize local registry: %s\n",
+ win_errstr(error));
+ return NULL;
+ }
+
+ return reg_import_hive_key(h, hive_root, -1, NULL);
+}
+
+struct registry_context *reg_common_open_local(struct cli_credentials *creds,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ WERROR error;
+ struct registry_context *h = NULL;
+
+ error = reg_open_samba(NULL, &h, ev_ctx, lp_ctx, NULL, creds);
+
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to open local registry:%s \n",
+ win_errstr(error));
+ return NULL;
+ }
+
+ return h;
+}
diff --git a/source4/lib/registry/tools/regdiff.c b/source4/lib/registry/tools/regdiff.c
new file mode 100644
index 0000000..aab7b68
--- /dev/null
+++ b/source4/lib/registry/tools/regdiff.c
@@ -0,0 +1,183 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Wilco Baan Hofman 2006
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/registry/tools/common.h"
+#include "param/param.h"
+
+enum reg_backend { REG_UNKNOWN, REG_LOCAL, REG_REMOTE, REG_NULL };
+
+static struct registry_context *open_backend(TALLOC_CTX *mem_ctx,
+ poptContext pc,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ enum reg_backend backend,
+ const char *remote_host)
+{
+ WERROR error;
+ struct registry_context *ctx;
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+
+ switch (backend) {
+ case REG_UNKNOWN:
+ poptPrintUsage(pc, stderr, 0);
+ return NULL;
+ case REG_LOCAL:
+ error = reg_open_samba(mem_ctx, &ctx, ev_ctx, lp_ctx, NULL,
+ creds);
+ break;
+ case REG_REMOTE:
+ error = reg_open_remote(mem_ctx, &ctx, NULL,
+ creds, lp_ctx,
+ remote_host, ev_ctx);
+ break;
+ case REG_NULL:
+ error = reg_open_local(mem_ctx, &ctx);
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error: %s\n", win_errstr(error));
+ return NULL;
+ }
+
+ return ctx;
+}
+
+int main(int argc, char **argv)
+{
+ const char **argv_const = discard_const_p(const char *, argv);
+ int opt;
+ poptContext pc;
+ char *outputfile = NULL;
+ enum reg_backend backend1 = REG_UNKNOWN, backend2 = REG_UNKNOWN;
+ const char *remote1 = NULL, *remote2 = NULL;
+ struct registry_context *h1 = NULL, *h2 = NULL;
+ WERROR error;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"output", 'o', POPT_ARG_STRING, &outputfile, 0, "output file to use", NULL },
+ {"null", 'n', POPT_ARG_NONE, NULL, 'n', "Diff from NULL", NULL },
+ {"remote", 'R', POPT_ARG_STRING, NULL, 'R', "Connect to remote server" , NULL },
+ {"local", 'L', POPT_ARG_NONE, NULL, 'L', "Open local registry", NULL },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ {0}
+ };
+ TALLOC_CTX *ctx;
+ void *callback_data;
+ struct tevent_context *ev_ctx;
+ struct reg_diff_callbacks *callbacks;
+ struct loadparm_context *lp_ctx = NULL;
+ bool ok;
+
+ ctx = talloc_init("regdiff");
+ if (ctx == NULL) {
+ exit(ENOMEM);
+ }
+
+ ok = samba_cmdline_init(ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(ctx);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(ctx);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ error = WERR_OK;
+ switch(opt) {
+ case 'L':
+ if (backend1 == REG_UNKNOWN)
+ backend1 = REG_LOCAL;
+ else if (backend2 == REG_UNKNOWN)
+ backend2 = REG_LOCAL;
+ break;
+ case 'n':
+ if (backend1 == REG_UNKNOWN)
+ backend1 = REG_NULL;
+ else if (backend2 == REG_UNKNOWN)
+ backend2 = REG_NULL;
+ break;
+ case 'R':
+ if (backend1 == REG_UNKNOWN) {
+ backend1 = REG_REMOTE;
+ remote1 = poptGetOptArg(pc);
+ } else if (backend2 == REG_UNKNOWN) {
+ backend2 = REG_REMOTE;
+ remote2 = poptGetOptArg(pc);
+ }
+ break;
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ }
+
+ ev_ctx = s4_event_context_init(ctx);
+ lp_ctx = samba_cmdline_get_lp_ctx();
+
+ h1 = open_backend(ctx, pc, ev_ctx, lp_ctx, backend1, remote1);
+ if (h1 == NULL)
+ return 1;
+
+ h2 = open_backend(ctx, pc, ev_ctx, lp_ctx, backend2, remote2);
+ if (h2 == NULL)
+ return 1;
+
+ poptFreeContext(pc);
+ samba_cmdline_burn(argc, argv);
+
+ error = reg_dotreg_diff_save(ctx, outputfile, &callbacks, &callback_data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Problem saving registry diff to '%s': %s\n",
+ outputfile, win_errstr(error));
+ return -1;
+ }
+
+ error = reg_generate_diff(h1, h2, callbacks, callback_data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Unable to generate diff between keys: %s\n",
+ win_errstr(error));
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/source4/lib/registry/tools/regpatch.c b/source4/lib/registry/tools/regpatch.c
new file mode 100644
index 0000000..eafaff6
--- /dev/null
+++ b/source4/lib/registry/tools/regpatch.c
@@ -0,0 +1,119 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) 2004-2007 Jelmer Vernooij, jelmer@samba.org
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/registry/registry.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/registry/tools/common.h"
+#include "param/param.h"
+#include "events/events.h"
+
+int main(int argc, char **argv)
+{
+ const char **argv_const = discard_const_p(const char *, argv);
+ bool ok;
+ TALLOC_CTX *mem_ctx = NULL;
+ int opt;
+ poptContext pc;
+ const char *patch;
+ struct registry_context *h;
+ const char *file = NULL;
+ const char *remote = NULL;
+ struct tevent_context *ev_ctx;
+ struct loadparm_context *lp_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"remote", 'R', POPT_ARG_STRING, &remote, 0, "connect to specified remote server", NULL},
+ {"file", 'F', POPT_ARG_STRING, &file, 0, "file path", NULL },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ mem_ctx = talloc_init("regtree.c/main");
+ if (mem_ctx == NULL) {
+ exit(ENOMEM);
+ }
+
+ ok = samba_cmdline_init(mem_ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ ev_ctx = s4_event_context_init(NULL);
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ creds = samba_cmdline_get_creds();
+
+ if (remote) {
+ h = reg_common_open_remote (remote, ev_ctx, lp_ctx, creds);
+ } else {
+ h = reg_common_open_local (creds, ev_ctx, lp_ctx);
+ }
+
+ if (h == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return 1;
+ }
+
+ patch = talloc_strdup(mem_ctx, poptGetArg(pc));
+ if (patch == NULL) {
+ poptPrintUsage(pc, stderr, 0);
+ TALLOC_FREE(mem_ctx);
+ return 1;
+ }
+
+ poptFreeContext(pc);
+ samba_cmdline_burn(argc, argv);
+
+ reg_diff_apply(h, patch);
+
+ TALLOC_FREE(mem_ctx);
+
+ return 0;
+}
diff --git a/source4/lib/registry/tools/regshell.c b/source4/lib/registry/tools/regshell.c
new file mode 100644
index 0000000..dc7bf54
--- /dev/null
+++ b/source4/lib/registry/tools/regshell.c
@@ -0,0 +1,708 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+ Copyright (C) Wilco Baan Hofman 2009
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/events/events.h"
+#include "system/time.h"
+#include "../libcli/smbreadline/smbreadline.h"
+#include "librpc/gen_ndr/ndr_security.h"
+#include "lib/registry/tools/common.h"
+#include "param/param.h"
+
+struct regshell_context {
+ struct registry_context *registry;
+ char *path;
+ char *predef;
+ struct registry_key *current;
+ struct registry_key *root;
+};
+
+static WERROR get_full_path(struct regshell_context *ctx, const char *path, char **ret_path)
+{
+ const char *dir;
+ char *tmp;
+ char *new_path;
+
+ if (path[0] == '\\') {
+ new_path = talloc_strdup(ctx, "");
+ } else {
+ new_path = talloc_strdup(ctx, ctx->path);
+ }
+
+ dir = strtok(discard_const_p(char, path), "\\");
+ if (dir == NULL) {
+ *ret_path = new_path;
+ return WERR_OK;
+ }
+ do {
+ if (strcmp(dir, "..") == 0) {
+ if (strchr(new_path, '\\')) {
+ new_path[strrchr(new_path, '\\') - new_path] = '\0';
+ } else {
+ tmp = new_path;
+ new_path = talloc_strdup(ctx, "");
+ talloc_free(tmp);
+ }
+ continue;
+ }
+ if (strcmp(dir, ".") == 0) {
+ continue;
+ }
+
+ tmp = new_path;
+ /* No prepending a backslash */
+ if (strcmp(new_path, "") == 0) {
+ new_path = talloc_strdup(ctx, dir);
+ } else {
+ new_path = talloc_asprintf(ctx, "%s\\%s", new_path, dir);
+ }
+ talloc_free(tmp);
+
+ } while ((dir = strtok(NULL, "\\")));
+
+ *ret_path = new_path;
+ return WERR_OK;
+}
+
+/* *
+ * ck/cd - change key
+ * ls - list values/keys
+ * rmval/rm - remove value
+ * rmkey/rmdir - remove key
+ * mkkey/mkdir - make key
+ * ch - change hive
+ * info - show key info
+ * save - save hive
+ * print - print value
+ * help
+ * exit
+ */
+
+static WERROR cmd_info(struct regshell_context *ctx, int argc, const char **argv)
+{
+ struct security_descriptor *sec_desc = NULL;
+ time_t last_mod;
+ WERROR error;
+ const char *classname = NULL;
+ NTTIME last_change;
+ uint32_t max_subkeynamelen;
+ uint32_t max_valnamelen;
+ uint32_t max_valbufsize;
+ uint32_t num_subkeys;
+ uint32_t num_values;
+
+ error = reg_key_get_info(ctx, ctx->current, &classname, &num_subkeys, &num_values,
+ &last_change, &max_subkeynamelen, &max_valnamelen, &max_valbufsize);
+ if (!W_ERROR_IS_OK(error)) {
+ printf("Error getting key info: %s\n", win_errstr(error));
+ return error;
+ }
+
+
+ printf("Name: %s\n", strchr(ctx->path, '\\')?strrchr(ctx->path, '\\')+1:
+ ctx->path);
+ printf("Full path: %s\n", ctx->path);
+ if (classname != NULL)
+ printf("Key Class: %s\n", classname);
+ last_mod = nt_time_to_unix(last_change);
+ printf("Time Last Modified: %s", ctime(&last_mod));
+ printf("Number of subkeys: %d\n", num_subkeys);
+ printf("Number of values: %d\n", num_values);
+
+ if (max_valnamelen > 0)
+ printf("Maximum value name length: %d\n", max_valnamelen);
+
+ if (max_valbufsize > 0)
+ printf("Maximum value data length: %d\n", max_valbufsize);
+
+ if (max_subkeynamelen > 0)
+ printf("Maximum sub key name length: %d\n", max_subkeynamelen);
+
+ error = reg_get_sec_desc(ctx, ctx->current, &sec_desc);
+ if (!W_ERROR_IS_OK(error)) {
+ printf("Error getting security descriptor: %s\n", win_errstr(error));
+ return WERR_OK;
+ }
+ NDR_PRINT_DEBUG(security_descriptor, sec_desc);
+ talloc_free(sec_desc);
+
+ return WERR_OK;
+}
+
+static WERROR cmd_predef(struct regshell_context *ctx, int argc, const char **argv)
+{
+ struct registry_key *ret = NULL;
+ if (argc < 2) {
+ fprintf(stderr, "Usage: predef predefined-key-name\n");
+ } else if (!ctx) {
+ fprintf(stderr, "No full registry loaded, no predefined keys defined\n");
+ } else {
+ WERROR error = reg_get_predefined_key_by_name(ctx->registry,
+ argv[1], &ret);
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error opening predefined key %s: %s\n",
+ argv[1], win_errstr(error));
+ return error;
+ }
+
+ ctx->predef = strupper_talloc(ctx, argv[1]);
+ ctx->current = ret;
+ ctx->root = ret;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_pwd(struct regshell_context *ctx,
+ int argc, const char **argv)
+{
+ if (ctx->predef) {
+ printf("%s\\", ctx->predef);
+ }
+ printf("%s\n", ctx->path);
+ return WERR_OK;
+}
+
+static WERROR cmd_set(struct regshell_context *ctx, int argc, const char **argv)
+{
+ struct registry_value val;
+ WERROR error;
+
+ if (argc < 4) {
+ fprintf(stderr, "Usage: set value-name type value\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (!reg_string_to_val(ctx, argv[2], argv[3], &val.data_type, &val.data)) {
+ fprintf(stderr, "Unable to interpret data\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ error = reg_val_set(ctx->current, argv[1], val.data_type, val.data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error setting value: %s\n", win_errstr(error));
+ return error;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_ck(struct regshell_context *ctx, int argc, const char **argv)
+{
+ struct registry_key *nkey = NULL;
+ char *full_path;
+ WERROR error;
+
+ if(argc == 2) {
+ if (!W_ERROR_IS_OK(get_full_path(ctx, argv[1], &full_path))) {
+ fprintf(stderr, "Unable to parse the supplied path\n");
+ return WERR_INVALID_PARAMETER;
+ }
+ error = reg_open_key(ctx->registry, ctx->root, full_path,
+ &nkey);
+ if(!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error opening specified key: %s\n",
+ win_errstr(error)));
+ return error;
+ }
+
+ talloc_free(ctx->path);
+ ctx->path = full_path;
+
+ ctx->current = nkey;
+ }
+ printf("New path is: %s\\%s\n", ctx->predef?ctx->predef:"", ctx->path);
+
+ return WERR_OK;
+}
+
+static WERROR cmd_print(struct regshell_context *ctx, int argc, const char **argv)
+{
+ uint32_t value_type;
+ DATA_BLOB value_data;
+ WERROR error;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: print <valuename>\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ error = reg_key_get_value_by_name(ctx, ctx->current, argv[1],
+ &value_type, &value_data);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "No such value '%s'\n", argv[1]);
+ return error;
+ }
+
+ printf("%s\n%s\n", str_regtype(value_type),
+ reg_val_data_string(ctx, value_type, value_data));
+
+ return WERR_OK;
+}
+
+static WERROR cmd_ls(struct regshell_context *ctx, int argc, const char **argv)
+{
+ unsigned int i;
+ WERROR error;
+ uint32_t valuetype;
+ DATA_BLOB valuedata;
+ const char *name = NULL;
+
+ for (i = 0; W_ERROR_IS_OK(error = reg_key_get_subkey_by_index(ctx,
+ ctx->current,
+ i,
+ &name,
+ NULL,
+ NULL)); i++) {
+ printf("K %s\n", name);
+ }
+
+ if (!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
+ fprintf(stderr, "Error occurred while browsing through keys: %s\n",
+ win_errstr(error));
+ return error;
+ }
+
+ for (i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(ctx,
+ ctx->current, i, &name, &valuetype, &valuedata)); i++)
+ printf("V \"%s\" %s %s\n", name, str_regtype(valuetype),
+ reg_val_data_string(ctx, valuetype, valuedata));
+
+ return WERR_OK;
+}
+static WERROR cmd_mkkey(struct regshell_context *ctx, int argc, const char **argv)
+{
+ struct registry_key *tmp;
+ WERROR error;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: mkkey <keyname>\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ error = reg_key_add_name(ctx, ctx->current, argv[1], 0, NULL, &tmp);
+
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error adding new subkey '%s': %s\n", argv[1],
+ win_errstr(error));
+ return error;
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_rmkey(struct regshell_context *ctx,
+ int argc, const char **argv)
+{
+ WERROR error;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: rmkey <name>\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ error = reg_key_del(ctx, ctx->current, argv[1]);
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error deleting '%s'\n", argv[1]);
+ return error;
+ } else {
+ fprintf(stderr, "Successfully deleted '%s'\n", argv[1]);
+ }
+
+ return WERR_OK;
+}
+
+static WERROR cmd_rmval(struct regshell_context *ctx, int argc, const char **argv)
+{
+ WERROR error;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: rmval <valuename>\n");
+ return WERR_INVALID_PARAMETER;
+ }
+
+ error = reg_del_value(ctx, ctx->current, argv[1]);
+ if(!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Error deleting value '%s'\n", argv[1]);
+ return error;
+ } else {
+ fprintf(stderr, "Successfully deleted value '%s'\n", argv[1]);
+ }
+
+ return WERR_OK;
+}
+
+_NORETURN_ static WERROR cmd_exit(struct regshell_context *ctx,
+ int argc, const char **argv)
+{
+ exit(0);
+}
+
+static WERROR cmd_help(struct regshell_context *ctx, int, const char **);
+
+static struct {
+ const char *name;
+ const char *alias;
+ const char *help;
+ WERROR (*handle)(struct regshell_context *ctx, int argc, const char **argv);
+} regshell_cmds[] = {
+ {"ck", "cd", "Change current key", cmd_ck },
+ {"info", "i", "Show detailed information of a key", cmd_info },
+ {"list", "ls", "List values/keys in current key", cmd_ls },
+ {"print", "p", "Print value", cmd_print },
+ {"mkkey", "mkdir", "Make new key", cmd_mkkey },
+ {"rmval", "rm", "Remove value", cmd_rmval },
+ {"rmkey", "rmdir", "Remove key", cmd_rmkey },
+ {"pwd", "pwk", "Printing current key", cmd_pwd },
+ {"set", "update", "Update value", cmd_set },
+ {"help", "?", "Help", cmd_help },
+ {"exit", "quit", "Exit", cmd_exit },
+ {"predef", "predefined", "Go to predefined key", cmd_predef },
+ {0}
+};
+
+static WERROR cmd_help(struct regshell_context *ctx,
+ int argc, const char **argv)
+{
+ unsigned int i;
+ printf("Available commands:\n");
+ for(i = 0; regshell_cmds[i].name; i++) {
+ printf("%s - %s\n", regshell_cmds[i].name,
+ regshell_cmds[i].help);
+ }
+ return WERR_OK;
+}
+
+static WERROR process_cmd(struct regshell_context *ctx,
+ char *line)
+{
+ int argc;
+ const char **argv = NULL;
+ int ret, i;
+
+ if ((ret = poptParseArgvString(line, &argc, &argv)) != 0) {
+ fprintf(stderr, "regshell: %s\n", poptStrerror(ret));
+ return WERR_INVALID_PARAMETER;
+ }
+
+ for(i = 0; regshell_cmds[i].name; i++) {
+ if(!strcmp(regshell_cmds[i].name, argv[0]) ||
+ (regshell_cmds[i].alias && !strcmp(regshell_cmds[i].alias, argv[0]))) {
+ return regshell_cmds[i].handle(ctx, argc, argv);
+ }
+ }
+
+ fprintf(stderr, "No such command '%s'\n", argv[0]);
+
+ return WERR_INVALID_PARAMETER;
+}
+
+#define MAX_COMPLETIONS 100
+
+static struct registry_key *current_key = NULL;
+
+static char **reg_complete_command(const char *text, int start, int end)
+{
+ /* Complete command */
+ char **matches;
+ size_t len, samelen=0;
+ size_t i, count = 1;
+
+ matches = malloc_array_p(char *, MAX_COMPLETIONS);
+ if (!matches) return NULL;
+ matches[0] = NULL;
+
+ len = strlen(text);
+ for (i=0;regshell_cmds[i].handle && count < MAX_COMPLETIONS-1;i++) {
+ if (strncmp(text, regshell_cmds[i].name, len) == 0) {
+ matches[count] = strdup(regshell_cmds[i].name);
+ if (!matches[count])
+ goto cleanup;
+ if (count == 1)
+ samelen = strlen(matches[count]);
+ else
+ while (strncmp(matches[count], matches[count-1], samelen) != 0)
+ samelen--;
+ count++;
+ }
+ }
+
+ switch (count) {
+ case 0: /* should never happen */
+ case 1:
+ goto cleanup;
+ case 2:
+ matches[0] = strdup(matches[1]);
+ break;
+ default:
+ matches[0] = strndup(matches[1], samelen);
+ }
+ matches[count] = NULL;
+ return matches;
+
+cleanup:
+ for (i = 0; i < count; i++) {
+ free(matches[i]);
+ }
+ free(matches);
+ return NULL;
+}
+
+static char **reg_complete_key(const char *text, int start, int end)
+{
+ struct registry_key *base;
+ const char *subkeyname;
+ unsigned int i, j = 1;
+ size_t len, samelen = 0;
+ char **matches;
+ const char *base_n = "";
+ TALLOC_CTX *mem_ctx;
+ WERROR status;
+ int ret;
+
+ matches = malloc_array_p(char *, MAX_COMPLETIONS);
+ if (!matches) return NULL;
+ matches[0] = NULL;
+ mem_ctx = talloc_init("completion");
+
+ base = current_key;
+
+ len = strlen(text);
+ for(i = 0; j < MAX_COMPLETIONS-1; i++) {
+ status = reg_key_get_subkey_by_index(mem_ctx, base, i,
+ &subkeyname, NULL, NULL);
+ if(W_ERROR_IS_OK(status)) {
+ if(!strncmp(text, subkeyname, len)) {
+ matches[j] = strdup(subkeyname);
+ j++;
+
+ if (j == 1)
+ samelen = strlen(matches[j]);
+ else
+ while (strncmp(matches[j], matches[j-1], samelen) != 0)
+ samelen--;
+ }
+ } else if(W_ERROR_EQUAL(status, WERR_NO_MORE_ITEMS)) {
+ break;
+ } else {
+ int n;
+
+ printf("Error creating completion list: %s\n",
+ win_errstr(status));
+
+ for (n = j; n >= 0; n--) {
+ SAFE_FREE(matches[n]);
+ }
+ SAFE_FREE(matches);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ }
+
+ if (j == 1) { /* No matches at all */
+ SAFE_FREE(matches);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ if (j == 2) { /* Exact match */
+ ret = asprintf(&matches[0], "%s%s", base_n, matches[1]);
+ } else {
+ ret = asprintf(&matches[0], "%s%s", base_n,
+ talloc_strndup(mem_ctx, matches[1], samelen));
+ }
+ talloc_free(mem_ctx);
+ if (ret == -1) {
+ SAFE_FREE(matches);
+ return NULL;
+ }
+
+ matches[j] = NULL;
+ return matches;
+}
+
+static char **reg_completion(const char *text, int start, int end)
+{
+ smb_readline_ca_char(' ');
+
+ if (start == 0) {
+ return reg_complete_command(text, start, end);
+ } else {
+ return reg_complete_key(text, start, end);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ const char **argv_const = discard_const_p(const char *, argv);
+ int opt;
+ const char *file = NULL;
+ poptContext pc;
+ const char *remote = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ struct loadparm_context *lp_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+ struct regshell_context *ctx;
+ struct tevent_context *ev_ctx;
+ bool ret = true;
+ bool ok;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"file", 'F', POPT_ARG_STRING, &file, 0, "open hive file", NULL },
+ {"remote", 'R', POPT_ARG_STRING, &remote, 0, "connect to specified remote server", NULL},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_LEGACY_S4
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ mem_ctx = talloc_init("regshell.c/main");
+ if (mem_ctx == NULL) {
+ exit(ENOMEM);
+ }
+
+ ok = samba_cmdline_init(mem_ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ poptFreeContext(pc);
+ samba_cmdline_burn(argc, argv);
+
+ ctx = talloc_zero(mem_ctx, struct regshell_context);
+
+ ev_ctx = s4_event_context_init(ctx);
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ creds = samba_cmdline_get_creds();
+
+ if (remote != NULL) {
+ ctx->registry = reg_common_open_remote(remote, ev_ctx,
+ lp_ctx,
+ creds);
+ } else if (file != NULL) {
+ ctx->current = reg_common_open_file(file, ev_ctx,
+ lp_ctx,
+ creds);
+ if (ctx->current == NULL) {
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+ ctx->registry = ctx->current->context;
+ ctx->path = talloc_strdup(ctx, "");
+ ctx->predef = NULL;
+ ctx->root = ctx->current;
+ } else {
+ ctx->registry = reg_common_open_local(creds,
+ ev_ctx,
+ lp_ctx);
+ }
+
+ if (ctx->registry == NULL) {
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ if (ctx->current == NULL) {
+ unsigned int i;
+
+ for (i = 0; (reg_predefined_keys[i].handle != 0) &&
+ (ctx->current == NULL); i++) {
+ WERROR err;
+ err = reg_get_predefined_key(ctx->registry,
+ reg_predefined_keys[i].handle,
+ &ctx->current);
+ if (W_ERROR_IS_OK(err)) {
+ ctx->predef = talloc_strdup(ctx,
+ reg_predefined_keys[i].name);
+ ctx->path = talloc_strdup(ctx, "");
+ ctx->root = ctx->current;
+ break;
+ } else {
+ ctx->current = NULL;
+ ctx->root = NULL;
+ }
+ }
+ }
+
+ if (ctx->current == NULL) {
+ fprintf(stderr, "Unable to access any of the predefined keys\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while (true) {
+ char *line, *prompt;
+
+ if (asprintf(&prompt, "%s\\%s> ", ctx->predef?ctx->predef:"",
+ ctx->path) < 0) {
+ ret = false;
+ break;
+ }
+
+ current_key = ctx->current; /* No way to pass a void * pointer
+ via readline :-( */
+ line = smb_readline(prompt, NULL, reg_completion);
+
+ if (line == NULL) {
+ free(prompt);
+ break;
+ }
+
+ if (line[0] != '\n') {
+ ret = W_ERROR_IS_OK(process_cmd(ctx, line));
+ }
+ free(line);
+ free(prompt);
+ }
+ TALLOC_FREE(mem_ctx);
+
+ return (ret?0:1);
+}
diff --git a/source4/lib/registry/tools/regtree.c b/source4/lib/registry/tools/regtree.c
new file mode 100644
index 0000000..1f0dac2
--- /dev/null
+++ b/source4/lib/registry/tools/regtree.c
@@ -0,0 +1,209 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) Jelmer Vernooij 2004-2007
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "lib/registry/tools/common.h"
+#include "lib/events/events.h"
+#include "lib/cmdline/cmdline.h"
+#include "param/param.h"
+
+/**
+ * Print a registry key recursively
+ *
+ * @param level Level at which to print
+ * @param p Key to print
+ * @param fullpath Whether the full pat hshould be printed or just the last bit
+ * @param novals Whether values should not be printed
+ */
+static void print_tree(unsigned int level, struct registry_key *p,
+ const char *name,
+ bool fullpath, bool novals)
+{
+ struct registry_key *subkey;
+ const char *valuename, *keyname;
+ uint32_t valuetype;
+ DATA_BLOB valuedata;
+ struct security_descriptor *sec_desc;
+ WERROR error;
+ unsigned int i;
+ TALLOC_CTX *mem_ctx;
+
+ for(i = 0; i < level; i++) putchar(' ');
+ puts(name);
+
+ mem_ctx = talloc_init("print_tree");
+ for (i = 0; W_ERROR_IS_OK(error = reg_key_get_subkey_by_index(mem_ctx,
+ p,
+ i,
+ &keyname,
+ NULL,
+ NULL)); i++) {
+
+ SMB_ASSERT(strlen(keyname) > 0);
+ if (!W_ERROR_IS_OK(reg_open_key(mem_ctx, p, keyname, &subkey)))
+ continue;
+
+ print_tree(level+1, subkey, (fullpath && strlen(name))?
+ talloc_asprintf(mem_ctx, "%s\\%s",
+ name, keyname):
+ keyname, fullpath, novals);
+ talloc_free(subkey);
+ }
+ talloc_free(mem_ctx);
+
+ if(!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
+ DEBUG(0, ("Error occurred while fetching subkeys for '%s': %s\n",
+ name, win_errstr(error)));
+ }
+
+ if (!novals) {
+ mem_ctx = talloc_init("print_tree");
+ for(i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(
+ mem_ctx, p, i, &valuename, &valuetype, &valuedata));
+ i++) {
+ unsigned int j;
+ for(j = 0; j < level+1; j++) putchar(' ');
+ printf("%s\n", reg_val_description(mem_ctx,
+ valuename, valuetype, valuedata));
+ }
+ talloc_free(mem_ctx);
+
+ if(!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) {
+ DEBUG(0, ("Error occurred while fetching values for '%s': %s\n",
+ name, win_errstr(error)));
+ }
+ }
+
+ mem_ctx = talloc_init("sec_desc");
+ if (!W_ERROR_IS_OK(reg_get_sec_desc(mem_ctx, p, &sec_desc))) {
+ DEBUG(0, ("Error getting security descriptor\n"));
+ }
+ talloc_free(mem_ctx);
+}
+
+int main(int argc, char **argv)
+{
+ const char **argv_const = discard_const_p(const char *, argv);
+ bool ok;
+ TALLOC_CTX *mem_ctx = NULL;
+ int opt;
+ unsigned int i;
+ const char *file = NULL;
+ const char *remote = NULL;
+ poptContext pc;
+ struct registry_context *h = NULL;
+ struct registry_key *start_key = NULL;
+ struct tevent_context *ev_ctx;
+ struct loadparm_context *lp_ctx = NULL;
+ struct cli_credentials *creds = NULL;
+ WERROR error;
+ bool fullpath = false, no_values = false;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"file", 'F', POPT_ARG_STRING, &file, 0, "file path", NULL },
+ {"remote", 'R', POPT_ARG_STRING, &remote, 0, "connect to specified remote server", NULL },
+ {"fullpath", 'f', POPT_ARG_NONE, &fullpath, 0, "show full paths", NULL},
+ {"no-values", 'V', POPT_ARG_NONE, &no_values, 0, "don't show values", NULL},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ mem_ctx = talloc_init("regtree.c/main");
+ if (mem_ctx == NULL) {
+ exit(ENOMEM);
+ }
+
+ ok = samba_cmdline_init(mem_ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ poptFreeContext(pc);
+ samba_cmdline_burn(argc, argv);
+
+ ev_ctx = s4_event_context_init(NULL);
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ creds = samba_cmdline_get_creds();
+
+ if (remote != NULL) {
+ h = reg_common_open_remote(remote, ev_ctx, lp_ctx, creds);
+ } else if (file != NULL) {
+ start_key = reg_common_open_file(file, ev_ctx, lp_ctx, creds);
+ } else {
+ h = reg_common_open_local(creds, ev_ctx, lp_ctx);
+ }
+
+ if (h == NULL && start_key == NULL) {
+ TALLOC_FREE(mem_ctx);
+ return 1;
+ }
+
+ error = WERR_OK;
+
+ if (start_key != NULL) {
+ print_tree(0, start_key, "", fullpath, no_values);
+ } else {
+ for(i = 0; reg_predefined_keys[i].handle; i++) {
+ error = reg_get_predefined_key(h,
+ reg_predefined_keys[i].handle,
+ &start_key);
+ if (!W_ERROR_IS_OK(error)) {
+ fprintf(stderr, "Skipping %s: %s\n",
+ reg_predefined_keys[i].name,
+ win_errstr(error));
+ continue;
+ }
+ SMB_ASSERT(start_key != NULL);
+ print_tree(0, start_key, reg_predefined_keys[i].name,
+ fullpath, no_values);
+ }
+ }
+
+ TALLOC_FREE(mem_ctx);
+ return 0;
+}
diff --git a/source4/lib/registry/util.c b/source4/lib/registry/util.c
new file mode 100644
index 0000000..1197adb
--- /dev/null
+++ b/source4/lib/registry/util.c
@@ -0,0 +1,302 @@
+/*
+ Unix SMB/CIFS implementation.
+ Transparent registry backend handling
+ Copyright (C) Jelmer Vernooij 2003-2007.
+ Copyright (C) Wilco Baan Hofman 2010.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "librpc/gen_ndr/winreg.h"
+#include "lib/util/data_blob.h"
+
+_PUBLIC_ char *reg_val_data_string(TALLOC_CTX *mem_ctx, uint32_t type,
+ const DATA_BLOB data)
+{
+ size_t converted_size = 0;
+ char *ret = NULL;
+
+ if (data.length == 0)
+ return talloc_strdup(mem_ctx, "");
+
+ switch (type) {
+ case REG_EXPAND_SZ:
+ case REG_SZ:
+ convert_string_talloc(mem_ctx,
+ CH_UTF16, CH_UNIX,
+ data.data, data.length,
+ (void **)&ret, &converted_size);
+ break;
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN:
+ SMB_ASSERT(data.length == sizeof(uint32_t));
+ ret = talloc_asprintf(mem_ctx, "0x%8.8x",
+ IVAL(data.data, 0));
+ break;
+ case REG_QWORD:
+ SMB_ASSERT(data.length == sizeof(uint64_t));
+ ret = talloc_asprintf(mem_ctx, "0x%16.16llx",
+ (long long)BVAL(data.data, 0));
+ break;
+ case REG_BINARY:
+ ret = data_blob_hex_string_upper(mem_ctx, &data);
+ break;
+ case REG_NONE:
+ /* "NULL" is the right return value */
+ break;
+ case REG_MULTI_SZ:
+ /* FIXME: We don't support this yet */
+ break;
+ default:
+ /* FIXME */
+ /* Other datatypes aren't supported -> return "NULL" */
+ break;
+ }
+
+ return ret;
+}
+
+/** Generate a string that describes a registry value */
+_PUBLIC_ char *reg_val_description(TALLOC_CTX *mem_ctx,
+ const char *name,
+ uint32_t data_type,
+ const DATA_BLOB data)
+{
+ return talloc_asprintf(mem_ctx, "%s = %s : %s", name?name:"<No Name>",
+ str_regtype(data_type),
+ reg_val_data_string(mem_ctx, data_type, data));
+}
+
+/*
+ * This implements reading hex bytes that include comma's.
+ * It was previously handled by strhex_to_data_blob, but that did not cover
+ * the format used by windows.
+ */
+static DATA_BLOB reg_strhex_to_data_blob(TALLOC_CTX *mem_ctx, const char *str)
+{
+ DATA_BLOB ret;
+ const char *HEXCHARS = "0123456789ABCDEF";
+ size_t i, j;
+ char *hi, *lo;
+
+ ret = data_blob_talloc_zero(mem_ctx, (strlen(str)+(strlen(str) % 3))/3);
+ j = 0;
+ for (i = 0; i < strlen(str); i++) {
+ hi = strchr(HEXCHARS, toupper(str[i]));
+ if (hi == NULL)
+ continue;
+
+ i++;
+ lo = strchr(HEXCHARS, toupper(str[i]));
+ if (lo == NULL)
+ break;
+
+ ret.data[j] = PTR_DIFF(hi, HEXCHARS) << 4;
+ ret.data[j] += PTR_DIFF(lo, HEXCHARS);
+ j++;
+
+ if (j > ret.length) {
+ DEBUG(0, ("Trouble converting hex string to bin\n"));
+ break;
+ }
+ }
+ return ret;
+}
+
+
+_PUBLIC_ bool reg_string_to_val(TALLOC_CTX *mem_ctx, const char *type_str,
+ const char *data_str, uint32_t *type, DATA_BLOB *data)
+{
+ char *tmp_type_str, *p, *q;
+ int result;
+
+ *type = regtype_by_string(type_str);
+
+ if (*type == -1) {
+ /* Normal windows format is hex, hex(type int as string),
+ dword or just a string. */
+ if (strncmp(type_str, "hex(", 4) == 0) {
+ /* there is a hex string with the value type between
+ the braces */
+ tmp_type_str = talloc_strdup(mem_ctx, type_str);
+ q = p = tmp_type_str + strlen("hex(");
+
+ /* Go to the closing brace or end of the string */
+ while (*q != ')' && *q != '\0') q++;
+ *q = '\0';
+
+ /* Convert hex string to int, store it in type */
+ result = sscanf(p, "%x", type);
+ if (!result) {
+ DEBUG(0, ("Could not convert hex to int\n"));
+ return false;
+ }
+ talloc_free(tmp_type_str);
+ } else if (strcmp(type_str, "hex") == 0) {
+ *type = REG_BINARY;
+ } else if (strcmp(type_str, "dword") == 0) {
+ *type = REG_DWORD;
+ }
+ }
+
+ if (*type == -1)
+ return false;
+
+ /* Convert data appropriately */
+
+ switch (*type) {
+ case REG_SZ:
+ return convert_string_talloc(mem_ctx,
+ CH_UNIX, CH_UTF16,
+ data_str, strlen(data_str)+1,
+ (void **)&data->data,
+ &data->length);
+ break;
+ case REG_MULTI_SZ:
+ case REG_EXPAND_SZ:
+ case REG_BINARY:
+ *data = reg_strhex_to_data_blob(mem_ctx, data_str);
+ break;
+ case REG_DWORD:
+ case REG_DWORD_BIG_ENDIAN: {
+ uint32_t tmp = strtol(data_str, NULL, 16);
+ *data = data_blob_talloc(mem_ctx, NULL, sizeof(uint32_t));
+ if (data->data == NULL) return false;
+ SIVAL(data->data, 0, tmp);
+ }
+ break;
+ case REG_QWORD: {
+ uint64_t tmp = strtoll(data_str, NULL, 16);
+ *data = data_blob_talloc(mem_ctx, NULL, sizeof(uint64_t));
+ if (data->data == NULL) return false;
+ SBVAL(data->data, 0, tmp);
+ }
+ break;
+ case REG_NONE:
+ ZERO_STRUCTP(data);
+ break;
+ default:
+ /* FIXME */
+ /* Other datatypes aren't supported -> return no success */
+ return false;
+ }
+ return true;
+}
+
+/** Open a key by name (including the predefined key name!) */
+WERROR reg_open_key_abs(TALLOC_CTX *mem_ctx, struct registry_context *handle,
+ const char *name, struct registry_key **result)
+{
+ struct registry_key *predef;
+ WERROR error;
+ size_t predeflength;
+ char *predefname;
+
+ if (strchr(name, '\\') != NULL)
+ predeflength = strchr(name, '\\')-name;
+ else
+ predeflength = strlen(name);
+
+ predefname = talloc_strndup(mem_ctx, name, predeflength);
+ W_ERROR_HAVE_NO_MEMORY(predefname);
+ error = reg_get_predefined_key_by_name(handle, predefname, &predef);
+ talloc_free(predefname);
+
+ if (!W_ERROR_IS_OK(error)) {
+ return error;
+ }
+
+ if (strchr(name, '\\')) {
+ return reg_open_key(mem_ctx, predef, strchr(name, '\\')+1,
+ result);
+ } else {
+ *result = predef;
+ return WERR_OK;
+ }
+}
+
+static WERROR get_abs_parent(TALLOC_CTX *mem_ctx, struct registry_context *ctx,
+ const char *path, struct registry_key **parent,
+ const char **name)
+{
+ char *parent_name;
+ WERROR error;
+
+ if (strchr(path, '\\') == NULL) {
+ return WERR_FOOBAR;
+ }
+
+ parent_name = talloc_strndup(mem_ctx, path, strrchr(path, '\\')-path);
+ W_ERROR_HAVE_NO_MEMORY(parent_name);
+ error = reg_open_key_abs(mem_ctx, ctx, parent_name, parent);
+ talloc_free(parent_name);
+ if (!W_ERROR_IS_OK(error)) {
+ return error;
+ }
+
+ *name = talloc_strdup(mem_ctx, strrchr(path, '\\')+1);
+ W_ERROR_HAVE_NO_MEMORY(*name);
+
+ return WERR_OK;
+}
+
+WERROR reg_key_del_abs(struct registry_context *ctx, const char *path)
+{
+ struct registry_key *parent;
+ const char *n;
+ TALLOC_CTX *mem_ctx = talloc_init("reg_key_del_abs");
+ WERROR error;
+
+ if (!strchr(path, '\\')) {
+ return WERR_FOOBAR;
+ }
+
+ error = get_abs_parent(mem_ctx, ctx, path, &parent, &n);
+ if (W_ERROR_IS_OK(error)) {
+ error = reg_key_del(mem_ctx, parent, n);
+ }
+
+ talloc_free(mem_ctx);
+
+ return error;
+}
+
+WERROR reg_key_add_abs(TALLOC_CTX *mem_ctx, struct registry_context *ctx,
+ const char *path, uint32_t access_mask,
+ struct security_descriptor *sec_desc,
+ struct registry_key **result)
+{
+ struct registry_key *parent;
+ const char *n;
+ WERROR error;
+
+ *result = NULL;
+
+ if (!strchr(path, '\\')) {
+ return WERR_ALREADY_EXISTS;
+ }
+
+ error = get_abs_parent(mem_ctx, ctx, path, &parent, &n);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(2, ("Opening parent of %s failed with %s\n", path,
+ win_errstr(error)));
+ return error;
+ }
+
+ error = reg_key_add_name(mem_ctx, parent, n, NULL, sec_desc, result);
+
+ return error;
+}
diff --git a/source4/lib/registry/wine.c b/source4/lib/registry/wine.c
new file mode 100644
index 0000000..77d2ce6
--- /dev/null
+++ b/source4/lib/registry/wine.c
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+ Registry interface
+ Copyright (C) Jelmer Vernooij 2007.
+
+ 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; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/registry/common/registry.h"
+#include "windows/registry.h"
+
+static WERROR wine_open_reg (struct registry_hive *h, struct registry_key **key)
+{
+ /* FIXME: Open h->location and mmap it */
+}
+
+static REG_OPS reg_backend_wine = {
+ .name = "wine",
+ .open_hive = wine_open_reg,
+
+};
+
+NTSTATUS registry_wine_init(void)
+{
+ register_backend("registry", &reg_backend_wine);
+ return NT_STATUS_OK;
+}
+
+WERROR reg_open_wine(struct registry_key **ctx)
+{
+ /* FIXME: Open ~/.wine/system.reg, etc */
+ return WERR_NOT_SUPPORTED;
+}
diff --git a/source4/lib/registry/wscript_build b/source4/lib/registry/wscript_build
new file mode 100644
index 0000000..2e01e43
--- /dev/null
+++ b/source4/lib/registry/wscript_build
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+
+bld.SAMBA_PIDL('PIDL_REG',
+ source='regf.idl',
+ options='--header --tdr-parser')
+
+bld.SAMBA_SUBSYSTEM('TDR_REGF',
+ source='tdr_regf.c',
+ public_deps='TDR'
+ )
+
+
+bld.SAMBA_LIBRARY('registry',
+ source='interface.c util.c samba.c patchfile_dotreg.c patchfile_preg.c patchfile.c regf.c hive.c local.c ldb.c rpc.c',
+ public_deps='dcerpc samba-util TDR_REGF ldb RPC_NDR_WINREG ldbsamba util_reg',
+ private_headers='registry.h',
+ private_library=True
+ )
+
+
+bld.SAMBA_SUBSYSTEM('registry_common',
+ source='tools/common.c',
+ autoproto='tools/common.h',
+ public_deps='registry'
+ )
+
+
+bld.SAMBA_BINARY('regdiff',
+ source='tools/regdiff.c',
+ manpages='man/regdiff.1',
+ deps='samba-hostconfig registry popt CMDLINE_S4'
+ )
+
+
+bld.SAMBA_BINARY('regpatch',
+ source='tools/regpatch.c',
+ manpages='man/regpatch.1',
+ deps='samba-hostconfig registry popt CMDLINE_S4 registry_common'
+ )
+
+
+bld.SAMBA_BINARY('regshell',
+ source='tools/regshell.c',
+ manpages='man/regshell.1',
+ deps='samba-hostconfig popt registry CMDLINE_S4 SMBREADLINE registry_common'
+ )
+
+
+bld.SAMBA_BINARY('regtree',
+ source='tools/regtree.c',
+ manpages='man/regtree.1',
+ deps='samba-hostconfig popt registry CMDLINE_S4 registry_common'
+ )
+
+
+bld.SAMBA_SUBSYSTEM('torture_registry',
+ source='tests/generic.c tests/hive.c tests/diff.c tests/registry.c',
+ autoproto='tests/proto.h',
+ deps='torture registry'
+ )
+
+pytalloc_util = bld.pyembed_libname('pytalloc-util')
+pyparam_util = bld.pyembed_libname('pyparam_util')
+
+bld.SAMBA_PYTHON('py_registry',
+ source='pyregistry.c',
+ public_deps='registry %s %s' % (pytalloc_util, pyparam_util),
+ realname='samba/registry.so'
+ )