diff options
Diffstat (limited to '')
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 = ®_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 = ®_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 = ®_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 <keyname></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 <keyname></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 <valname></term> + <listitem><para> + Delete the specified value. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>rmkey|rmdir <keyname></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, ®_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", ®type)) + 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 = ®_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(®f_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 = ®_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", ®_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' + ) |