summaryrefslogtreecommitdiff
path: root/source4/lib/registry/patchfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/lib/registry/patchfile.c')
-rw-r--r--source4/lib/registry/patchfile.c469
1 files changed, 469 insertions, 0 deletions
diff --git a/source4/lib/registry/patchfile.c b/source4/lib/registry/patchfile.c
new file mode 100644
index 0000000000..0ede3106f0
--- /dev/null
+++ b/source4/lib/registry/patchfile.c
@@ -0,0 +1,469 @@
+/*
+ Unix SMB/CIFS implementation.
+ Reading registry patch files
+
+ 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 "system/filesys.h"
+#include "param/param.h"
+
+
+_PUBLIC_ WERROR reg_preg_diff_load(int fd,
+ struct smb_iconv_convenience *iconv_convenience,
+ const struct reg_diff_callbacks *callbacks,
+ void *callback_data);
+
+_PUBLIC_ WERROR reg_dotreg_diff_load(int fd,
+ struct smb_iconv_convenience *iconv_convenience,
+ 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)
+{
+ 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 occured while getting key info: %s\n",
+ win_errstr(error)));
+ return error;
+ }
+ } else {
+ old_num_subkeys = 0;
+ old_num_values = 0;
+ }
+
+ /* Subkeys that were 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 occured while getting subkey by index: %s\n",
+ win_errstr(error2)));
+ continue;
+ }
+
+ if (newkey != NULL) {
+ error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
+
+ if (W_ERROR_IS_OK(error2))
+ continue;
+ } else {
+ error2 = WERR_BADFILE;
+ t2 = NULL;
+ }
+
+ if (!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
+ DEBUG(0, ("Error occured while getting subkey by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ /* newkey didn't have such a subkey, add del diff */
+ tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
+ callbacks->del_key(callback_data, tmppath);
+ 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 occured while getting key info: %s\n",
+ win_errstr(error)));
+ 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 occured 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 {
+ t1 = NULL;
+ error2 = WERR_BADFILE;
+ }
+
+ if (!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
+ DEBUG(0, ("Error occured while getting subkey by name: %s\n",
+ win_errstr(error2)));
+ talloc_free(mem_ctx);
+ return error2;
+ }
+
+ /* oldkey didn't have such a subkey, add add diff */
+ tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
+ callbacks->add_key(callback_data, tmppath);
+
+ W_ERROR_NOT_OK_RETURN(
+ reg_open_key(mem_ctx, newkey, keyname1, &t2));
+
+ reg_generate_diff_key(t1, t2, tmppath,
+ callbacks, callback_data);
+ talloc_free(tmppath);
+ }
+
+ /* Values that were changed */
+ for(i = 0; i < new_num_values; i++) {
+ const char *name;
+ uint32_t type1, type2;
+ DATA_BLOB contents1, contents2;
+
+ 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 key 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_BADFILE;
+
+ if(!W_ERROR_IS_OK(error2) &&
+ !W_ERROR_EQUAL(error2, WERR_BADFILE)) {
+ DEBUG(0, ("Error occured 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)
+ continue;
+
+ callbacks->set_value(callback_data, path, name,
+ type1, contents1);
+ }
+
+ /* Values that were deleted */
+ for (i = 0; i < old_num_values; i++) {
+ const char *name;
+ error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &name,
+ NULL, NULL);
+ if (!W_ERROR_IS_OK(error1)) {
+ DEBUG(0, ("Error ocurred getting value by index: %s\n",
+ win_errstr(error1)));
+ talloc_free(mem_ctx);
+ return error1;
+ }
+
+ error2 = reg_key_get_value_by_name(mem_ctx, newkey, name, NULL,
+ NULL);
+
+ if (W_ERROR_IS_OK(error2))
+ continue;
+
+ if (!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
+ DEBUG(0, ("Error occured while getting value by name: %s\n",
+ win_errstr(error2)));
+ return error2;
+ }
+
+ callbacks->del_value(callback_data, path, name);
+ }
+
+ 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)
+{
+ int i;
+ WERROR error;
+
+ for(i = HKEY_FIRST; i <= HKEY_LAST; i++) {
+ struct registry_key *r1 = NULL, *r2 = NULL;
+ error = reg_get_predefined_key(ctx1, i, &r1);
+ if (!W_ERROR_IS_OK(error) &&
+ !W_ERROR_EQUAL(error, WERR_BADFILE)) {
+ DEBUG(0, ("Unable to open hive %s for backend 1\n",
+ reg_get_predef_name(i)));
+ }
+
+ error = reg_get_predefined_key(ctx2, i, &r2);
+ if (!W_ERROR_IS_OK(error) &&
+ !W_ERROR_EQUAL(error, WERR_BADFILE)) {
+ DEBUG(0, ("Unable to open hive %s for backend 2\n",
+ reg_get_predef_name(i)));
+ }
+
+ if (r1 == NULL && r2 == NULL)
+ continue;
+
+ error = reg_generate_diff_key(r1, r2, reg_get_predef_name(i),
+ 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,
+ struct smb_iconv_convenience *iconv_convenience,
+ 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_GENERAL_FAILURE;
+ }
+
+ if (read(fd, &hdr, 4) != 4) {
+ DEBUG(0, ("Error reading registry patch file `%s'\n",
+ filename));
+ return WERR_GENERAL_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, iconv_convenience, callbacks, callback_data);
+ } else {
+ /* Must be a normal .REG file */
+ return reg_dotreg_diff_load(fd, iconv_convenience, 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);
+ 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++ = '\\';
+ }
+ }
+
+ /* 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;
+ }
+ return WERR_OK;
+}
+
+static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name)
+{
+ struct registry_context *ctx = (struct registry_context *)_ctx;
+ WERROR error;
+
+ error = reg_key_del_abs(ctx, key_name);
+
+ if(!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Unable to delete key '%s'\n", key_name));
+ return error;
+ }
+
+ 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_BADFILE)) {
+ 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;
+ }
+
+ 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(tmp, value_name);
+ if (!W_ERROR_IS_OK(error)) {
+ DEBUG(0, ("Error deleting value '%s'\n", value_name));
+ return error;
+ }
+
+
+ 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;
+ int i;
+ uint32_t num_values;
+
+ 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, &num_values, NULL, NULL, NULL, NULL));
+
+ for (i = 0; i < num_values; i++) {
+ const char *name;
+ W_ERROR_NOT_OK_RETURN(reg_key_get_value_by_index(ctx, key, i,
+ &name,
+ NULL, NULL));
+ W_ERROR_NOT_OK_RETURN(reg_del_value(key, name));
+ }
+
+ 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, lp_iconv_convenience(global_loadparm),
+ &callbacks, ctx);
+}