diff options
Diffstat (limited to '')
-rw-r--r-- | source4/lib/registry/patchfile.c | 543 |
1 files changed, 543 insertions, 0 deletions
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); +} |