diff options
Diffstat (limited to 'source4/lib')
46 files changed, 6769 insertions, 3545 deletions
diff --git a/source4/lib/basic.mk b/source4/lib/basic.mk index 0f61ac6549..e993207b00 100644 --- a/source4/lib/basic.mk +++ b/source4/lib/basic.mk @@ -5,6 +5,7 @@ include charset/config.mk include ldb/config.mk include tls/config.mk include registry/config.mk +include policy/config.mk include messaging/config.mk include events/config.mk include cmdline/config.mk diff --git a/source4/lib/policy/adm.h b/source4/lib/policy/adm.h new file mode 100644 index 0000000000..5751261824 --- /dev/null +++ b/source4/lib/policy/adm.h @@ -0,0 +1,48 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) 2006 Wilco Baan Hofman <wilco@baanhofman.nl> + Copyright (C) 2006 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 2 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. +*/ + +#ifndef __ADM_H__ +#define __ADM_H__ + +struct adm_file { + struct adm_class *classes; +}; + +struct adm_class { + struct adm_category *categories; +}; + +struct adm_category { + struct adm_category *subcategories; + struct adm_policy *policies; +}; + +struct adm_policy { + struct adm_part *parts; + +}; + +struct adm_part { + +}; + +struct adm_file *adm_read_file(const char *); + +#endif /* __ADM_H__ */ diff --git a/source4/lib/policy/config.mk b/source4/lib/policy/config.mk new file mode 100644 index 0000000000..f404d58377 --- /dev/null +++ b/source4/lib/policy/config.mk @@ -0,0 +1,12 @@ +[LIBRARY::LIBPOLICY] +CFLAGS = -Iheimdal/lib/roken +OBJ_FILES = lex.o parse_adm.o +PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBSAMBA-CONFIG LIBTALLOC CHARSET + +lib/policy/lex.l: lib/policy/parse_adm.h + +lib/policy/parse_adm.h: lib/policy/parse_adm.c + +[BINARY::dumpadm] +OBJ_FILES = dumpadm.o +PRIVATE_DEPENDENCIES = LIBPOLICY LIBPOPT LIBSAMBA-CONFIG LIBTALLOC LIBSAMBA-UTIL CHARSET diff --git a/source4/lib/policy/dumpadm.c b/source4/lib/policy/dumpadm.c new file mode 100644 index 0000000000..aba09150d7 --- /dev/null +++ b/source4/lib/policy/dumpadm.c @@ -0,0 +1,54 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) 2006 Wilco Baan Hofman <wilco@baanhofman.nl> + Copyright (C) 2006 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 2 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/popt/popt.h" +#include "lib/policy/adm.h" + +int main(int argc, char **argv) +{ + BOOL ret = True; + poptContext pc; + struct poptOption long_options[] = { + POPT_AUTOHELP + { 0, 0, 0, 0 } + }; + + pc = poptGetContext(argv[0], argc, (const char **)argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "<ADM-FILE> ..."); + + while ((poptGetNextOpt(pc) != -1)) + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + exit(1); + } + + while (poptPeekArg(pc)) { + const char *name = poptGetArg(pc); + + adm_read_file(name); + } + + poptFreeContext(pc); + + return ret; +} diff --git a/source4/lib/policy/lex.l b/source4/lib/policy/lex.l new file mode 100644 index 0000000000..1157bca2f7 --- /dev/null +++ b/source4/lib/policy/lex.l @@ -0,0 +1,142 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) 2006 Wilco Baan Hofman <wilco@baanhofman.nl> + Copyright (C) 2006 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 2 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/policy/parse_adm.h" +void error_message (const char *format, ...); +int yyparse (void); + +static int lineno = 1; +static bool utf16 = false; + +#define YY_INPUT(buf,result,max_size) \ +{ \ + if (utf16) { \ + uint16_t v; \ + if (fread(&v, 2, 1, yyin) < 1) \ + result = YY_NULL; \ + else \ + result = push_codepoint(buf, v); \ + } else { \ + int c = getc(yyin); \ + result = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \ + } \ +} + +%} + +%% + +ACTIONLIST { return ACTIONLIST; } +CATEGORY { return CATEGORY; } +CHECKBOX { return CHECKBOX; } +CLASS { return CLASS; } +DELETE { return DEL; } +DEFAULT { return DEFAULT; } +DROPDOWNLIST { return DROPDOWNLIST; } +EDITTEXT { return EDITTEXT; } +END { return END; } +EXPLAIN { return EXPLAIN; } +ITEMLIST { return ITEMLIST; } +KEYNAME { return KEYNAME; } +MACHINE { return MACHINE; } +MIN { return MINIMUM; } +MAX { return MAXIMUM; } +NAME { return NAME; } +NUMERIC { return NUMERIC; } +PART { return PART; } +POLICY { return POLICY; } +REQUIRED { return REQUIRED; } +SPIN { return SPIN; } +SUPPORTED { return SUPPORTED; } +TEXT { return TEXT; } +USER { return USER; } +VALUE { return VALUE; } +VALUENAME { return VALUENAME; } +VALUEON { return VALUEON; } +VALUEOFF { return VALUEOFF; } += { return EQUALS; } +\[strings\] { return STRINGSSECTION; } + +[0-9]+ { + char *e, *y = yytext; + yylval.integer = strtol((const char *)yytext, &e, 0); + if(e == y) + error_message("malformed constant (%s)", yytext); + else + return INTEGER; + } + +[A-Za-z\\{}][{}\-\\A-Za-z0-9_]* { + yylval.text = strdup ((const char *)yytext); + return LITERAL; + } + +"!!"[A-Za-z][-A-Za-z0-9_]* { + yylval.text = strdup ((const char *)yytext); + return LOOKUPLITERAL; + } +[ \t]+ +\n { lineno++; } +;[^\n]*\n { lineno++; } +\"([^\n]+)\n { lineno++; yylval.text = strdup((const char *)yytext); return LITERAL; } +%% + +#ifndef yywrap /* XXX */ +int +yywrap () +{ + return 1; +} +#endif + + +void +error_message (const char *format, ...) +{ + va_list args; + + va_start (args, format); + fprintf (stderr, "%d:", lineno); + vfprintf (stderr, format, args); + va_end (args); +} + +struct adm_file *adm_read_file(const char *file) +{ + uint8_t c[2]; + yyin = fopen(file, "r"); + if (yyin == NULL) + return NULL; + + c[0] = getc(yyin); + c[1] = getc(yyin); + if (c[0] == 0xff && c[1] == 0xfe) { + utf16 = true; + } else { + rewind(yyin); + } + + yyparse(); + + return NULL; /* FIXME */ +} diff --git a/source4/lib/policy/parse_adm.y b/source4/lib/policy/parse_adm.y new file mode 100644 index 0000000000..450625f58a --- /dev/null +++ b/source4/lib/policy/parse_adm.y @@ -0,0 +1,138 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) 2006 Wilco Baan Hofman <wilco@baanhofman.nl> + Copyright (C) 2006 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 2 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. + + For more information on the .ADM file format: + http://msdn2.microsoft.com/en-us/library/aa372405.aspx +*/ + +%{ +#include "config.h" +void error_message (const char *format, ...); +int yyparse (void); +void yyerror (const char *s); +extern int yylex (void); + +%} + +%union { + char *text; + int integer; +} + +%token CATEGORY +%token CLASS +%token USER +%token MACHINE +%token POLICY +%token KEYNAME +%token EXPLAIN +%token VALUENAME +%token VALUEON VALUEOFF +%token PART +%token ITEMLIST +%token NAME +%token VALUE +%token NUMERIC EDITTEXT TEXT DROPDOWNLIST CHECKBOX +%token MINIMUM MAXIMUM DEFAULT +%token END +%token ACTIONLIST +%token DEL +%token SUPPORTED +%token <text> LITERAL +%token <integer> INTEGER +%token <text> LOOKUPLITERAL +%token CLIENTEXT +%token REQUIRED +%token NOSORT +%token SPIN +%token EQUALS +%token STRINGSSECTION + +%start admfile + +%% + +admfile: classes strings; + +classes: /* empty */ | class classes; + +class: CLASS classvalue categories; +classvalue: USER|MACHINE; + +categories: /* empty */ | category categories; + +string: LITERAL | LOOKUPLITERAL; + +category: CATEGORY string categoryitems END CATEGORY; + +categoryitem: explain | category | policy | keyname; +categoryitems: categoryitem categoryitems | /* empty */ ; + +policy: POLICY string policyitems END POLICY; +policyitem: explain | keyname | valuename | valueon | valueoff | min | max | defaultvalue | supported | part; +policyitems: policyitem policyitems | /* empty */; + +valuetype: NUMERIC | EDITTEXT | TEXT | DROPDOWNLIST | CHECKBOX; + +part: PART string valuetype partitems END PART; + +spin: SPIN INTEGER; + +partitem: keyname | valuename | valueon | valueoff | min | max | defaultvalue | itemlist | REQUIRED | spin; +partitems: partitem partitems | /* empty */; + +min: MINIMUM INTEGER; +max: MAXIMUM INTEGER; +defaultvalue: DEFAULT INTEGER; + +explain: EXPLAIN string; +value: DEL | NUMERIC INTEGER; + +valueon: VALUEON value; +valueoff: VALUEOFF value; + +valuename: VALUENAME string; +keyname: KEYNAME string; + +itemlist: ITEMLIST items END ITEMLIST; +itemname: NAME string; +itemvalue: VALUE value; + +item: itemname | itemvalue | DEFAULT | actionlist; +items: /* empty */ | item items; + +supported: SUPPORTED string; + +actionlist: ACTIONLIST actions END ACTIONLIST; +actions: valuename actions | itemvalue actions | /* empty */; + +variable: LITERAL EQUALS LITERAL; +variables: variable variables | /* empty */; +strings: STRINGSSECTION variables; + +%% + +void +yyerror (const char *s) +{ + error_message ("%s\n", s); +} + + + diff --git a/source4/lib/registry/Doxyfile b/source4/lib/registry/Doxyfile index ff591b6fe4..efc01cd355 100644 --- a/source4/lib/registry/Doxyfile +++ b/source4/lib/registry/Doxyfile @@ -15,7 +15,7 @@ WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" -INPUT = . common +INPUT = . FILE_PATTERNS = *.c *.h *.dox GENERATE_HTML = YES HTML_OUTPUT = html diff --git a/source4/lib/registry/README b/source4/lib/registry/README index db1fb7a678..07b2c01684 100644 --- a/source4/lib/registry/README +++ b/source4/lib/registry/README @@ -1,30 +1,31 @@ This is the registry library. The registry is basically a bunch of -hives that can be loaded from different places. +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. -The various registry backends provide support for loading/saving -specific types of hives: +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 - - w95 (USER.DAT-style files) - - nt4 (NTUSER.DAT-style files) - - gconf (GNOME configuration) + - regf (NTUSER.DAT-style files) - rpc (Remote individual hives) + - directory -Instead of opening individual hives, one can also open a 'complete' -registry by using one of these three functions: - - - reg_open_local() - load local registry, see below - - reg_open_remote() - connect to remote registry over RPC - - reg_open_wine() (not working yet) - -reg_open_local() loads a set of hives based on smb.conf settings. +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 = nt4:NTUSER.DAT +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. diff --git a/source4/lib/registry/TODO b/source4/lib/registry/TODO index 562ed5657e..5f1e7d034b 100644 --- a/source4/lib/registry/TODO +++ b/source4/lib/registry/TODO @@ -4,10 +4,10 @@ reg_backend_dir: - value support -reg_backend_w95.c: +reg_backend_creg.c: - write support -reg_backend_nt4: +reg_backend_regf: - write support reg_backend_rpc: diff --git a/source4/lib/registry/common/reg_interface.c b/source4/lib/registry/common/reg_interface.c deleted file mode 100644 index 6ee8a726dc..0000000000 --- a/source4/lib/registry/common/reg_interface.c +++ /dev/null @@ -1,584 +0,0 @@ -/* - Unix SMB/CIFS implementation. - Transparent registry backend handling - Copyright (C) Jelmer Vernooij 2003-2004. - - 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 "build.h" - -/** - * @file - * @brief Main registry functions - */ - -/* List of available backends */ -static struct reg_init_function_entry *backends = NULL; - -static struct reg_init_function_entry *reg_find_backend_entry(const char *name); - -/** Register a new backend. */ -_PUBLIC_ NTSTATUS registry_register(const void *_hive_ops) -{ - const struct hive_operations *hive_ops = _hive_ops; - struct reg_init_function_entry *entry = backends; - - DEBUG(5,("Attempting to register registry backend %s\n", hive_ops->name)); - - /* Check for duplicates */ - if (reg_find_backend_entry(hive_ops->name)) { - DEBUG(0,("There already is a registry backend registered with the name %s!\n", hive_ops->name)); - return NT_STATUS_OBJECT_NAME_COLLISION; - } - - entry = talloc(talloc_autofree_context(), struct reg_init_function_entry); - entry->hive_functions = hive_ops; - - DLIST_ADD(backends, entry); - DEBUG(5,("Successfully added registry backend '%s'\n", hive_ops->name)); - return NT_STATUS_OK; -} - -/** Find a backend in the list of available backends */ -static struct reg_init_function_entry *reg_find_backend_entry(const char *name) -{ - struct reg_init_function_entry *entry; - - entry = backends; - - while(entry) { - if (strcmp(entry->hive_functions->name, name) == 0) return entry; - entry = entry->next; - } - - return NULL; -} - -/** Initialize the registry subsystem */ -_PUBLIC_ NTSTATUS registry_init(void) -{ - init_module_fn static_init[] = STATIC_registry_MODULES; - init_module_fn *shared_init = load_samba_modules(NULL, "registry"); - - run_init_functions(static_init); - run_init_functions(shared_init); - - talloc_free(shared_init); - - return NT_STATUS_OK; -} - -/** Check whether a certain backend is present. */ -_PUBLIC_ BOOL reg_has_backend(const char *backend) -{ - return reg_find_backend_entry(backend) != NULL?True:False; -} - -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 a list of predefined keys. */ -_PUBLIC_ int reg_list_predefs(TALLOC_CTX *mem_ctx, char ***predefs, uint32_t **hkeys) -{ - int i; - *predefs = talloc_array(mem_ctx, char *, ARRAY_SIZE(reg_predefined_keys)); - *hkeys = talloc_array(mem_ctx, uint32_t, ARRAY_SIZE(reg_predefined_keys)); - - for (i = 0; reg_predefined_keys[i].name; i++) { - (*predefs)[i] = talloc_strdup(mem_ctx, reg_predefined_keys[i].name); - (*hkeys)[i] = reg_predefined_keys[i].handle; - } - - return i; -} - -/** Obtain name of specific hkey. */ -_PUBLIC_ const char *reg_get_predef_name(uint32_t hkey) -{ - 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) -{ - 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_BADFILE; -} - -/** Get predefined key by id. */ -_PUBLIC_ WERROR reg_get_predefined_key(struct registry_context *ctx, uint32_t hkey, struct registry_key **key) -{ - WERROR ret = ctx->get_predefined_key(ctx, hkey, key); - - if (W_ERROR_IS_OK(ret)) { - (*key)->name = talloc_strdup(*key, reg_get_predef_name(hkey)); - (*key)->path = ""; - } - - return ret; -} - -/** Open a registry file/host/etc */ -_PUBLIC_ WERROR reg_open_hive(TALLOC_CTX *parent_ctx, const char *backend, const char *location, struct auth_session_info *session_info, struct cli_credentials *credentials, struct registry_key **root) -{ - struct registry_hive *rethive; - struct registry_key *retkey = NULL; - struct reg_init_function_entry *entry; - WERROR werr; - - entry = reg_find_backend_entry(backend); - - if (!entry) { - DEBUG(0, ("No such registry backend '%s' loaded!\n", backend)); - return WERR_GENERAL_FAILURE; - } - - if(!entry->hive_functions || !entry->hive_functions->open_hive) { - return WERR_NOT_SUPPORTED; - } - - rethive = talloc(parent_ctx, struct registry_hive); - rethive->location = location?talloc_strdup(rethive, location):NULL; - rethive->session_info = talloc_reference(rethive, session_info); - rethive->credentials = talloc_reference(rethive, credentials); - rethive->functions = entry->hive_functions; - rethive->backend_data = NULL; - - werr = entry->hive_functions->open_hive(rethive, &retkey); - - if(!W_ERROR_IS_OK(werr)) { - return werr; - } - - if(!retkey) { - DEBUG(0, ("Backend %s didn't provide root key!\n", backend)); - return WERR_GENERAL_FAILURE; - } - - rethive->root = retkey; - - retkey->hive = rethive; - retkey->name = NULL; - retkey->path = talloc_strdup(retkey, ""); - - *root = retkey; - - return WERR_OK; -} - -/** - * 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) -{ - WERROR error; - - if(!parent) { - DEBUG(0, ("Invalid parent key specified for open of '%s'\n", name)); - return WERR_INVALID_PARAM; - } - - if(!parent->hive->functions->open_key && - (parent->hive->functions->get_subkey_by_name || - parent->hive->functions->get_subkey_by_index)) { - char *orig = strdup(name), - *curbegin = orig, - *curend = strchr(orig, '\\'); - struct registry_key *curkey = parent; - - while(curbegin && *curbegin) { - if(curend)*curend = '\0'; - error = reg_key_get_subkey_by_name(mem_ctx, curkey, curbegin, &curkey); - if(!W_ERROR_IS_OK(error)) { - SAFE_FREE(orig); - return error; - } - if(!curend) break; - curbegin = curend + 1; - curend = strchr(curbegin, '\\'); - } - SAFE_FREE(orig); - - *result = curkey; - - return WERR_OK; - } - - if(!parent->hive->functions->open_key) { - DEBUG(0, ("Registry backend doesn't have get_subkey_by_name nor open_key!\n")); - return WERR_NOT_SUPPORTED; - } - - error = parent->hive->functions->open_key(mem_ctx, parent, name, result); - - if(!W_ERROR_IS_OK(error)) return error; - - (*result)->hive = parent->hive; - (*result)->path = ((parent->hive->root == parent)?talloc_strdup(mem_ctx, name):talloc_asprintf(mem_ctx, "%s\\%s", parent->path, name)); - (*result)->hive = parent->hive; - - return WERR_OK; -} - -/** - * Get value by index - */ -_PUBLIC_ WERROR reg_key_get_value_by_index(TALLOC_CTX *mem_ctx, const struct registry_key *key, int idx, struct registry_value **val) -{ - if(!key) return WERR_INVALID_PARAM; - - if(key->hive->functions->get_value_by_index) { - WERROR status = key->hive->functions->get_value_by_index(mem_ctx, key, idx, val); - if(!W_ERROR_IS_OK(status)) - return status; - } else { - return WERR_NOT_SUPPORTED; - } - - return WERR_OK; -} - -/** - * Get the number of subkeys. - */ -_PUBLIC_ WERROR reg_key_num_subkeys(const struct registry_key *key, uint32_t *count) -{ - if(!key) return WERR_INVALID_PARAM; - - if(key->hive->functions->num_subkeys) { - return key->hive->functions->num_subkeys(key, count); - } - - if(key->hive->functions->get_subkey_by_index) { - int i; - WERROR error; - struct registry_key *dest = NULL; - TALLOC_CTX *mem_ctx = talloc_init("num_subkeys"); - - for(i = 0; W_ERROR_IS_OK(error = reg_key_get_subkey_by_index(mem_ctx, key, i, &dest)); i++); - talloc_free(mem_ctx); - - *count = i; - if(W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) error = WERR_OK; - return error; - } - - return WERR_NOT_SUPPORTED; -} - -/** - * Get the number of values of a key. - */ -_PUBLIC_ WERROR reg_key_num_values(const struct registry_key *key, uint32_t *count) -{ - - if(!key) return WERR_INVALID_PARAM; - - if (key->hive->functions->num_values) { - return key->hive->functions->num_values(key, count); - } - - if(key->hive->functions->get_value_by_index) { - int i; - WERROR error; - struct registry_value *dest; - TALLOC_CTX *mem_ctx = talloc_init("num_subkeys"); - - for(i = 0; W_ERROR_IS_OK(error = key->hive->functions->get_value_by_index(mem_ctx, key, i, &dest)); i++); - talloc_free(mem_ctx); - - *count = i; - if(W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) error = WERR_OK; - return error; - } - - return WERR_NOT_SUPPORTED; -} - -/** - * Get subkey by index. - */ -_PUBLIC_ WERROR reg_key_get_subkey_by_index(TALLOC_CTX *mem_ctx, const struct registry_key *key, int idx, struct registry_key **subkey) -{ - if(!key) return WERR_INVALID_PARAM; - - if(key->hive->functions->get_subkey_by_index) { - WERROR status = key->hive->functions->get_subkey_by_index(mem_ctx, key, idx, subkey); - if(!NT_STATUS_IS_OK(status)) return status; - } else { - return WERR_NOT_SUPPORTED; - } - - if(key->hive->root == key) - (*subkey)->path = talloc_strdup(mem_ctx, (*subkey)->name); - else - (*subkey)->path = talloc_asprintf(mem_ctx, "%s\\%s", key->path, (*subkey)->name); - - (*subkey)->hive = key->hive; - return WERR_OK;; -} - -/** - * Get subkey by name. - */ -WERROR reg_key_get_subkey_by_name(TALLOC_CTX *mem_ctx, const struct registry_key *key, const char *name, struct registry_key **subkey) -{ - int i; - WERROR error = WERR_OK; - - if(!key) return WERR_INVALID_PARAM; - - if(key->hive->functions->get_subkey_by_name) { - error = key->hive->functions->get_subkey_by_name(mem_ctx, key,name,subkey); - } else if(key->hive->functions->open_key) { - error = key->hive->functions->open_key(mem_ctx, key, name, subkey); - } else if(key->hive->functions->get_subkey_by_index) { - for(i = 0; W_ERROR_IS_OK(error); i++) { - error = reg_key_get_subkey_by_index(mem_ctx, key, i, subkey); - if(W_ERROR_IS_OK(error) && !strcasecmp((*subkey)->name, name)) { - break; - } - } - - if (W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) - error = WERR_DEST_NOT_FOUND; - } else { - return WERR_NOT_SUPPORTED; - } - - if(!W_ERROR_IS_OK(error)) return error; - - (*subkey)->path = talloc_asprintf(mem_ctx, "%s\\%s", key->path, (*subkey)->name); - (*subkey)->hive = key->hive; - - return WERR_OK; -} - -/** - * Get value by name. - */ -_PUBLIC_ WERROR reg_key_get_value_by_name(TALLOC_CTX *mem_ctx, const struct registry_key *key, const char *name, struct registry_value **val) -{ - int i; - WERROR error = WERR_OK; - - if(!key) return WERR_INVALID_PARAM; - - if(key->hive->functions->get_value_by_name) { - error = key->hive->functions->get_value_by_name(mem_ctx, key,name, val); - } else { - for(i = 0; W_ERROR_IS_OK(error); i++) { - error = reg_key_get_value_by_index(mem_ctx, key, i, val); - if(W_ERROR_IS_OK(error) && !strcasecmp((*val)->name, name)) { - break; - } - } - } - - if (W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) - return WERR_DEST_NOT_FOUND; - - return error; -} - -/** - * Delete a key. - */ -_PUBLIC_ WERROR reg_key_del(struct registry_key *parent, const char *name) -{ - WERROR error; - if(!parent) return WERR_INVALID_PARAM; - - - if(!parent->hive->functions->del_key) - return WERR_NOT_SUPPORTED; - - error = parent->hive->functions->del_key(parent, name); - if(!W_ERROR_IS_OK(error)) return error; - - return WERR_OK; -} - -/** - * Add a key. - */ -_PUBLIC_ WERROR reg_key_add_name(TALLOC_CTX *mem_ctx, const struct registry_key *parent, const char *name, uint32_t access_mask, struct security_descriptor *desc, struct registry_key **newkey) -{ - WERROR error; - - if (!parent) return WERR_INVALID_PARAM; - - if (!parent->hive->functions->add_key) { - DEBUG(1, ("Backend '%s' doesn't support method add_key\n", parent->hive->functions->name)); - return WERR_NOT_SUPPORTED; - } - - error = parent->hive->functions->add_key(mem_ctx, parent, name, access_mask, desc, newkey); - - if(!W_ERROR_IS_OK(error)) return error; - - if (!*newkey) { - DEBUG(0, ("Backend returned WERR_OK, but didn't specify key!\n")); - return WERR_GENERAL_FAILURE; - } - - (*newkey)->hive = parent->hive; - - return WERR_OK; -} - -/** - * Set a value. - */ -_PUBLIC_ WERROR reg_val_set(struct registry_key *key, const char *value, uint32_t type, DATA_BLOB data) -{ - /* A 'real' set function has preference */ - if (key->hive->functions->set_value) - return key->hive->functions->set_value(key, value, type, data); - - DEBUG(1, ("Backend '%s' doesn't support method set_value\n", key->hive->functions->name)); - return WERR_NOT_SUPPORTED; -} - -/** - * 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) -{ - /* A 'real' set function has preference */ - if (key->hive->functions->key_get_sec_desc) - return key->hive->functions->key_get_sec_desc(ctx, key, secdesc); - - DEBUG(1, ("Backend '%s' doesn't support method get_sec_desc\n", key->hive->functions->name)); - return WERR_NOT_SUPPORTED; -} - -/** - * Delete a value. - */ -_PUBLIC_ WERROR reg_del_value(const struct registry_key *key, const char *valname) -{ - WERROR ret = WERR_OK; - if(!key->hive->functions->del_value) - return WERR_NOT_SUPPORTED; - - ret = key->hive->functions->del_value(key, valname); - - if(!W_ERROR_IS_OK(ret)) return ret; - - return ret; -} - -/** - * Flush a key to disk. - */ -_PUBLIC_ WERROR reg_key_flush(const struct registry_key *key) -{ - if (!key) { - return WERR_INVALID_PARAM; - } - - if (key->hive->functions->flush_key) { - return key->hive->functions->flush_key(key); - } - - /* No need for flushing, apparently */ - return WERR_OK; -} - -/** - * Get the maximum name and data lengths of the subkeys. - */ -_PUBLIC_ WERROR reg_key_subkeysizes(const struct registry_key *key, uint32_t *max_subkeylen, uint32_t *max_subkeysize) -{ - int i = 0; - struct registry_key *subkey; - WERROR error; - TALLOC_CTX *mem_ctx = talloc_init("subkeysize"); - - *max_subkeylen = *max_subkeysize = 0; - - do { - error = reg_key_get_subkey_by_index(mem_ctx, key, i, &subkey); - - if (W_ERROR_IS_OK(error)) { - *max_subkeysize = MAX(*max_subkeysize, 0xFF); - *max_subkeylen = MAX(*max_subkeylen, strlen(subkey->name)); - } - - i++; - } while (W_ERROR_IS_OK(error)); - - talloc_free(mem_ctx); - - return WERR_OK; -} - -/** - * Get the maximum name and data lengths of the values. - */ -_PUBLIC_ WERROR reg_key_valuesizes(const struct registry_key *key, uint32_t *max_valnamelen, uint32_t *max_valbufsize) -{ - int i = 0; - struct registry_value *value; - WERROR error; - TALLOC_CTX *mem_ctx = talloc_init("subkeysize"); - - *max_valnamelen = *max_valbufsize = 0; - - do { - error = reg_key_get_value_by_index(mem_ctx, key, i, &value); - - if (W_ERROR_IS_OK(error)) { - if (value->name) { - *max_valnamelen = MAX(*max_valnamelen, strlen(value->name)); - } - *max_valbufsize = MAX(*max_valbufsize, value->data.length); - } - - i++; - } while (W_ERROR_IS_OK(error)); - - talloc_free(mem_ctx); - - return WERR_OK; -} diff --git a/source4/lib/registry/config.mk b/source4/lib/registry/config.mk index cea37e7e2f..de276d2b5e 100644 --- a/source4/lib/registry/config.mk +++ b/source4/lib/registry/config.mk @@ -1,23 +1,10 @@ -# Registry backends - -################################################ -# Start MODULE registry_nt4 -[MODULE::registry_nt4] -INIT_FUNCTION = registry_nt4_init -SUBSYSTEM = registry -OBJ_FILES = \ - reg_backend_nt4.o -PRIVATE_DEPENDENCIES = TDR_REGF -# End MODULE registry_nt4 -################################################ - [SUBSYSTEM::TDR_REGF] PUBLIC_DEPENDENCIES = TDR OBJ_FILES = tdr_regf.o # Special support for external builddirs -lib/registry/reg_backend_nt4.c: lib/registry/tdr_regf.c -$(srcdir)/lib/registry/reg_backend_nt4.c: lib/registry/tdr_regf.c +lib/registry/regf.c: lib/registry/tdr_regf.c +$(srcdir)/lib/registry/regf.c: lib/registry/tdr_regf.c lib/registry/tdr_regf.h: lib/registry/tdr_regf.c lib/registry/tdr_regf.c: $(srcdir)/lib/registry/regf.idl @CPP="$(CPP)" srcdir="$(srcdir)" $(PERL) $(srcdir)/pidl/pidl $(PIDL_ARGS) \ @@ -28,67 +15,36 @@ clean:: @-rm -f lib/registry/regf.h lib/registry/tdr_regf* ################################################ -# Start MODULE registry_w95 -[MODULE::registry_w95] -INIT_FUNCTION = registry_w95_init -SUBSYSTEM = registry -OBJ_FILES = \ - reg_backend_w95.o -# End MODULE registry_w95 -################################################ - -################################################ -# Start MODULE registry_dir -[MODULE::registry_dir] -INIT_FUNCTION = registry_dir_init -SUBSYSTEM = registry -OBJ_FILES = \ - reg_backend_dir.o -PRIVATE_DEPENDENCIES = LIBTALLOC -# End MODULE registry_dir -################################################ - -################################################ -# Start MODULE registry_rpc -[MODULE::registry_rpc] -INIT_FUNCTION = registry_rpc_init -OUTPUT_TYPE = INTEGRATED -SUBSYSTEM = registry -OBJ_FILES = \ - reg_backend_rpc.o -PRIVATE_DEPENDENCIES = RPC_NDR_WINREG -# End MODULE registry_rpc -################################################ - -################################################ -# Start MODULE registry_ldb -[MODULE::registry_ldb] -INIT_FUNCTION = registry_ldb_init -SUBSYSTEM = registry -OBJ_FILES = \ - reg_backend_ldb.o -PRIVATE_DEPENDENCIES = \ - LIBLDB -# End MODULE registry_ldb -################################################ - -################################################ # Start SUBSYSTEM registry [LIBRARY::registry] VERSION = 0.0.1 SO_VERSION = 0 DESCRIPTION = Windows-style registry library OBJ_FILES = \ - common/reg_interface.o \ - common/reg_util.o \ - reg_samba.o \ - patchfile.o -PRIVATE_DEPENDENCIES = \ - LIBSAMBA-UTIL CHARSET + interface.o \ + util.o \ + samba.o \ + patchfile_dotreg.o \ + patchfile_preg.o \ + patchfile.o \ + regf.o \ + hive.o \ + local.o \ + ldb.o \ + dir.o \ + rpc.o +PUBLIC_DEPENDENCIES = \ + LIBSAMBA-UTIL CHARSET TDR_REGF LIBLDB \ + RPC_NDR_WINREG PUBLIC_HEADERS = registry.h # End MODULE registry_ldb ################################################ +[SUBSYSTEM::registry_common] +PUBLIC_DEPENDENCIES = registry +OBJ_FILES = tools/common.o +PUBLIC_PROTO_HEADER = tools/common.h + ################################################ # Start BINARY regdiff [BINARY::regdiff] @@ -106,7 +62,8 @@ MANPAGE = man/regdiff.1 INSTALLDIR = BINDIR OBJ_FILES = tools/regpatch.o PRIVATE_DEPENDENCIES = \ - LIBSAMBA-CONFIG registry LIBPOPT POPT_SAMBA POPT_CREDENTIALS + LIBSAMBA-CONFIG registry LIBPOPT POPT_SAMBA POPT_CREDENTIALS \ + registry_common MANPAGE = man/regpatch.1 # End BINARY regpatch ################################################ @@ -118,7 +75,7 @@ INSTALLDIR = BINDIR OBJ_FILES = tools/regshell.o PRIVATE_DEPENDENCIES = \ LIBSAMBA-CONFIG LIBPOPT registry POPT_SAMBA POPT_CREDENTIALS \ - SMBREADLINE + SMBREADLINE registry_common MANPAGE = man/regshell.1 # End BINARY regshell ################################################ @@ -129,7 +86,8 @@ MANPAGE = man/regshell.1 INSTALLDIR = BINDIR OBJ_FILES = tools/regtree.o PRIVATE_DEPENDENCIES = \ - LIBSAMBA-CONFIG LIBPOPT registry POPT_SAMBA POPT_CREDENTIALS + LIBSAMBA-CONFIG LIBPOPT registry POPT_SAMBA POPT_CREDENTIALS \ + registry_common MANPAGE = man/regtree.1 # End BINARY regtree ################################################ diff --git a/source4/lib/registry/dir.c b/source4/lib/registry/dir.c new file mode 100644 index 0000000000..146c5197fd --- /dev/null +++ b/source4/lib/registry/dir.c @@ -0,0 +1,333 @@ +/* + Unix SMB/CIFS implementation. + Registry interface + 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 "hive.h" +#include "system/dir.h" +#include "system/filesys.h" + +struct dir_key { + struct hive_key key; + const char *path; +}; + +static struct hive_operations reg_backend_dir; + +static WERROR reg_dir_add_key(TALLOC_CTX *mem_ctx, + const struct hive_key *parent, + const char *name, const char *classname, + struct security_descriptor *desc, + struct hive_key **result) +{ + struct dir_key *dk = talloc_get_type(parent, struct dir_key); + char *path; + int ret; + + path = talloc_asprintf(mem_ctx, "%s/%s", dk->path, name); + ret = mkdir(path, 0700); + if (ret == 0) { + struct dir_key *key = talloc(mem_ctx, struct dir_key); + key->key.ops = ®_backend_dir; + key->path = talloc_steal(key, path); + *result = (struct hive_key *)key; + return WERR_OK; + } + + if (errno == EEXIST) + return WERR_ALREADY_EXISTS; + printf("FAILED %s BECAUSE: %s\n", path, strerror(errno)); + return WERR_GENERAL_FAILURE; +} + +static WERROR reg_dir_del_key(const struct hive_key *k, const char *name) +{ + struct dir_key *dk = talloc_get_type(k, struct dir_key); + char *child = talloc_asprintf(NULL, "%s/%s", dk->path, name); + WERROR ret; + + if (rmdir(child) == 0) + ret = WERR_OK; + else if (errno == ENOENT) + ret = WERR_NOT_FOUND; + else + ret = WERR_GENERAL_FAILURE; + + talloc_free(child); + + return ret; +} + +static WERROR reg_dir_open_key(TALLOC_CTX *mem_ctx, + const struct hive_key *parent, + const char *name, struct hive_key **subkey) +{ + DIR *d; + char *fullpath; + const struct dir_key *p = talloc_get_type(parent, struct dir_key); + struct dir_key *ret; + + if (name == NULL) { + DEBUG(0, ("NULL pointer passed as directory name!")); + return WERR_INVALID_PARAM; + } + + fullpath = talloc_asprintf(mem_ctx, "%s/%s", p->path, name); + + d = opendir(fullpath); + if (d == NULL) { + DEBUG(3,("Unable to open '%s': %s\n", fullpath, strerror(errno))); + return WERR_BADFILE; + } + closedir(d); + ret = talloc(mem_ctx, struct dir_key); + ret->key.ops = ®_backend_dir; + ret->path = talloc_steal(ret, fullpath); + *subkey = (struct hive_key *)ret; + return WERR_OK; +} + +static WERROR reg_dir_key_by_index(TALLOC_CTX *mem_ctx, + const struct hive_key *k, uint32_t idx, + const char **name, + const char **classname, + NTTIME *last_mod_time) +{ + struct dirent *e; + const struct dir_key *dk = talloc_get_type(k, struct dir_key); + int i = 0; + DIR *d; + + d = opendir(dk->path); + + if (d == NULL) + return WERR_INVALID_PARAM; + + while((e = readdir(d))) { + if(!ISDOT(e->d_name) && !ISDOTDOT(e->d_name)) { + struct stat stbuf; + char *thispath; + + /* Check if file is a directory */ + asprintf(&thispath, "%s/%s", dk->path, e->d_name); + stat(thispath, &stbuf); + + if (!S_ISDIR(stbuf.st_mode)) { + SAFE_FREE(thispath); + continue; + } + + if (i == idx) { + struct stat st; + *name = talloc_strdup(mem_ctx, e->d_name); + *classname = NULL; + stat(thispath, &st); + unix_to_nt_time(last_mod_time, st.st_mtime); + SAFE_FREE(thispath); + closedir(d); + return WERR_OK; + } + i++; + + SAFE_FREE(thispath); + } + } + + closedir(d); + + return WERR_NO_MORE_ITEMS; +} + +WERROR reg_open_directory(TALLOC_CTX *parent_ctx, + const char *location, struct hive_key **key) +{ + struct dir_key *dk; + + if (location == NULL) + return WERR_INVALID_PARAM; + + dk = talloc(parent_ctx, struct dir_key); + dk->key.ops = ®_backend_dir; + dk->path = talloc_strdup(dk, location); + *key = (struct hive_key *)dk; + return WERR_OK; +} + +WERROR reg_create_directory(TALLOC_CTX *parent_ctx, + const char *location, struct hive_key **key) +{ + if (mkdir(location, 0700) != 0) { + *key = NULL; + return WERR_GENERAL_FAILURE; + } + + return reg_open_directory(parent_ctx, location, key); +} + +static WERROR reg_dir_get_info(TALLOC_CTX *ctx, const struct hive_key *key, + const char **classname, + uint32_t *num_subkeys, + uint32_t *num_values, + NTTIME *lastmod) +{ + DIR *d; + const struct dir_key *dk = talloc_get_type(key, struct dir_key); + struct dirent *e; + struct stat st; + + SMB_ASSERT(key != NULL); + + if (classname != NULL) + *classname = NULL; + + d = opendir(dk->path); + if (d == NULL) + return WERR_INVALID_PARAM; + + if (num_subkeys != NULL) + *num_subkeys = 0; + + if (num_values != NULL) + *num_values = 0; + + while((e = readdir(d))) { + if(!ISDOT(e->d_name) && !ISDOTDOT(e->d_name)) { + char *path = talloc_asprintf(ctx, "%s/%s", dk->path, e->d_name); + + if (stat(path, &st) < 0) { + DEBUG(0, ("Error statting %s: %s\n", path, strerror(errno))); + continue; + } + + if (S_ISDIR(st.st_mode) && num_subkeys != NULL) + (*num_subkeys)++; + + if (!S_ISDIR(st.st_mode) && num_values != NULL) + (*num_values)++; + + talloc_free(path); + } + } + + closedir(d); + + if (lastmod != NULL) + *lastmod = 0; + return WERR_OK; +} + +static WERROR reg_dir_set_value (struct hive_key *key, const char *name, + uint32_t type, const DATA_BLOB data) +{ + const struct dir_key *dk = talloc_get_type(key, struct dir_key); + char *path = talloc_asprintf(dk, "%s/%s", dk->path, name); + + if (!file_save(path, data.data, data.length)) + return WERR_GENERAL_FAILURE; + + /* FIXME: Type */ + + return WERR_OK; +} + +static WERROR reg_dir_get_value (TALLOC_CTX *mem_ctx, + struct hive_key *key, const char *name, + uint32_t *type, DATA_BLOB *data) +{ + const struct dir_key *dk = talloc_get_type(key, struct dir_key); + char *path = talloc_asprintf(mem_ctx, "%s/%s", dk->path, name); + size_t size; + char *contents; + + contents = file_load(path, &size, mem_ctx); + talloc_free(path); + if (contents == NULL) + return WERR_NOT_FOUND; + + if (type != NULL) + *type = 4; /* FIXME */ + + data->data = (uint8_t *)contents; + data->length = size; + + return WERR_OK; +} + +static WERROR reg_dir_enum_value (TALLOC_CTX *mem_ctx, + const struct hive_key *key, int idx, + const char **name, + uint32_t *type, DATA_BLOB *data) +{ + const struct dir_key *dk = talloc_get_type(key, struct dir_key); + DIR *d; + struct dirent *e; + int i; + + d = opendir(dk->path); + if (d == NULL) { + DEBUG(3,("Unable to open '%s': %s\n", dk->path, strerror(errno))); + return WERR_BADFILE; + } + + i = 0; + while((e = readdir(d))) { + if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) + continue; + + if (i == idx) { + if (name != NULL) + *name = talloc_strdup(mem_ctx, e->d_name); + W_ERROR_NOT_OK_RETURN(reg_dir_get_value(mem_ctx, key, *name, type, data)); + return WERR_OK; + } + + i++; + } + closedir(d); + + return WERR_NO_MORE_ITEMS; +} + + +static WERROR reg_dir_del_value (struct hive_key *key, const char *name) +{ + const struct dir_key *dk = talloc_get_type(key, struct dir_key); + char *path = talloc_asprintf(key, "%s/%s", dk->path, name); + if (unlink(path) < 0) { + talloc_free(path); + if (errno == ENOENT) + return WERR_NOT_FOUND; + return WERR_GENERAL_FAILURE; + } + talloc_free(path); + + return WERR_OK; +} + +static struct hive_operations reg_backend_dir = { + .name = "dir", + .get_key_by_name = reg_dir_open_key, + .get_key_info = reg_dir_get_info, + .add_key = reg_dir_add_key, + .del_key = reg_dir_del_key, + .enum_key = reg_dir_key_by_index, + .set_value = reg_dir_set_value, + .get_value_by_name = reg_dir_get_value, + .enum_value = reg_dir_enum_value, + .delete_value = reg_dir_del_value, +}; diff --git a/source4/lib/registry/hive.c b/source4/lib/registry/hive.c new file mode 100644 index 0000000000..b2c826b93d --- /dev/null +++ b/source4/lib/registry/hive.c @@ -0,0 +1,145 @@ + +/* + 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 2 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 "hive.h" +#include "system/filesys.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 hive_key **root) +{ + int fd, num; + char peek[20]; + + /* Check for directory */ + if (directory_exist(location)) { + return reg_open_directory(parent_ctx, location, root); + } + + fd = open(location, O_RDWR); + if (fd == -1) { + return WERR_BADFILE; + } + + num = read(fd, peek, 20); + if (num == -1) { + return WERR_BADFILE; + } + + if (!strncmp(peek, "regf", 4)) { + close(fd); + return reg_open_regf_file(parent_ctx, location, root); + } else if (!strncmp(peek, "TDB file", 8)) { + close(fd); + return reg_open_ldb_file(parent_ctx, location, session_info, credentials, root); + } + + return WERR_BADFILE; +} + +_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) +{ + return key->ops->get_key_info(mem_ctx, key, classname, num_subkeys, + num_values, last_change_time); +} + +_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(const struct hive_key *key, const char *name) +{ + return key->ops->del_key(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_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_del_value (struct hive_key *key, const char *name) +{ + if (key->ops->delete_value == NULL) + return WERR_NOT_SUPPORTED; + + return key->ops->delete_value(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/hive.h b/source4/lib/registry/hive.h new file mode 100644 index 0000000000..33759fdecc --- /dev/null +++ b/source4/lib/registry/hive.h @@ -0,0 +1,197 @@ +/* + 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 2 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. +*/ + +#ifndef __REGISTRY_HIVE_H__ +#define __REGISTRY_HIVE_H__ + +#include "core.h" +#include "talloc.h" +#include "librpc/gen_ndr/security.h" + +/** + * This file contains 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 *name, + const char *classname, struct security_descriptor *desc, + struct hive_key **key); + /** + * Remove an existing key. + */ + WERROR (*del_key) (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, + const struct hive_key *key, int 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) (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); +}; + +struct cli_credentials; +struct auth_session_info; + +WERROR reg_open_hive(TALLOC_CTX *parent_ctx, const char *location, + struct auth_session_info *session_info, + struct cli_credentials *credentials, + 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); +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); +_PUBLIC_ WERROR hive_key_del(const struct hive_key *key, const char *name); +_PUBLIC_ 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_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_del_value (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 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); + + +#endif /* __REGISTRY_HIVE_H__ */ diff --git a/source4/lib/registry/interface.c b/source4/lib/registry/interface.c new file mode 100644 index 0000000000..4d75e99f00 --- /dev/null +++ b/source4/lib/registry/interface.c @@ -0,0 +1,277 @@ +/* + 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" +#include "build.h" + + +/** + * @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) +{ + 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) +{ + 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_BADFILE; +} + +/** Get predefined key by id. */ +_PUBLIC_ WERROR reg_get_predefined_key(const 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_PARAM; + } + + 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_PARAM; + + 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) +{ + if (key == NULL) + return WERR_INVALID_PARAM; + + 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); +} + +/** + * Get subkey by index. + */ +_PUBLIC_ WERROR reg_key_get_subkey_by_index(TALLOC_CTX *mem_ctx, + const struct registry_key *key, int idx, const char **name, + const char **keyclass, NTTIME *last_changed_time) +{ + if (key == NULL) + return WERR_INVALID_PARAM; + + 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_PARAM; + + 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(struct registry_key *parent, const char *name) +{ + if (parent == NULL) + return WERR_INVALID_PARAM; + + if (parent->context->ops->delete_key == NULL) + return WERR_NOT_SUPPORTED; + + return parent->context->ops->delete_key(parent, name); +} + +/** + * Add a key. + */ +_PUBLIC_ WERROR reg_key_add_name(TALLOC_CTX *mem_ctx, + struct registry_key *parent, + const char *name, const char *key_class, + struct security_descriptor *desc, + struct registry_key **newkey) +{ + if (parent == NULL) + return WERR_INVALID_PARAM; + + 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, name, + 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_PARAM; + + /* 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_PARAM; + + /* A 'real' set function has preference */ + if (key->context->ops->get_security == NULL) + return WERR_NOT_SUPPORTED; + + return key->context->ops->get_security(ctx, key, secdesc); +} + +/** + * Delete a value. + */ +_PUBLIC_ WERROR reg_del_value(struct registry_key *key, const char *valname) +{ + if (key == NULL) + return WERR_INVALID_PARAM; + + if (key->context->ops->delete_value == NULL) + return WERR_NOT_SUPPORTED; + + return key->context->ops->delete_value(key, valname); +} + +/** + * Flush a key to disk. + */ +_PUBLIC_ WERROR reg_key_flush(struct registry_key *key) +{ + if (key == NULL) + return WERR_INVALID_PARAM; + + if (key->context->ops->flush_key == NULL) + return WERR_NOT_SUPPORTED; + + return key->context->ops->flush_key(key); +} diff --git a/source4/lib/registry/ldb.c b/source4/lib/registry/ldb.c new file mode 100644 index 0000000000..8ee4d9f932 --- /dev/null +++ b/source4/lib/registry/ldb.c @@ -0,0 +1,501 @@ +/* + Unix SMB/CIFS implementation. + Registry interface + 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 "lib/ldb/include/ldb.h" +#include "lib/ldb/include/ldb_errors.h" +#include "db_wrap.h" +#include "librpc/gen_ndr/winreg.h" + +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; + int subkey_count, value_count; +}; + +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; + if (name != NULL) + *name = talloc_strdup(mem_ctx, ldb_msg_find_attr_as_string(msg, "value", NULL)); + + if (type != NULL) + *type = ldb_msg_find_attr_as_uint(msg, "type", 0); + val = ldb_msg_find_ldb_val(msg, "data"); + + switch (*type) + { + case REG_SZ: + case REG_EXPAND_SZ: + data->length = convert_string_talloc(mem_ctx, CH_UTF8, CH_UTF16, + val->data, val->length, (void **)&data->data); + break; + + case REG_DWORD: { + uint32_t tmp = strtoul((char *)val->data, NULL, 0); + *data = data_blob_talloc(mem_ctx, &tmp, 4); + } + break; + + default: + *data = data_blob_talloc(mem_ctx, val->data, val->length); + 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_val val; + struct ldb_message *msg = talloc_zero(mem_ctx, struct ldb_message); + char *type_s; + + ldb_msg_add_string(msg, "value", talloc_strdup(mem_ctx, name)); + + switch (type) { + case REG_SZ: + case REG_EXPAND_SZ: + val.length = convert_string_talloc(mem_ctx, CH_UTF16, CH_UTF8, + (void *)data.data, data.length, (void **)&val.data); + ldb_msg_add_value(msg, "data", &val, NULL); + break; + + case REG_DWORD: + ldb_msg_add_string(msg, "data", talloc_asprintf(mem_ctx, "0x%x", IVAL(data.data, 0))); + break; + default: + ldb_msg_add_value(msg, "data", &data, NULL); + } + + + type_s = talloc_asprintf(mem_ctx, "%u", type); + ldb_msg_add_string(msg, "type", type_s); + + return msg; +} + + +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) +{ + TALLOC_CTX *local_ctx; + struct ldb_dn *ret; + char *mypath = talloc_strdup(mem_ctx, path); + char *begin; + struct ldb_key_data *kd = talloc_get_type(from, struct ldb_key_data); + struct ldb_context *ldb = kd->ldb; + + local_ctx = talloc_new(mem_ctx); + + if (add) { + ret = ldb_dn_new(mem_ctx, ldb, add); + } else { + ret = ldb_dn_new(mem_ctx, ldb, NULL); + } + if (!ldb_dn_validate(ret)) { + talloc_free(ret); + talloc_free(local_ctx); + return NULL; + } + + while (mypath) { + char *keyname; + + begin = strrchr(mypath, '\\'); + + if (begin) keyname = begin + 1; + else keyname = mypath; + + if(strlen(keyname)) { + ldb_dn_add_base_fmt(ret, "key=%s", keyname); + } + + if(begin) { + *begin = '\0'; + } else { + break; + } + } + + ldb_dn_add_base(ret, kd->dn); + + talloc_free(local_ctx); + + 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, kd->dn, LDB_SCOPE_ONELEVEL, "(key=*)", NULL, &res); + + 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, kd->dn, LDB_SCOPE_ONELEVEL, "(value=*)", NULL, &res); + + 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_message_element *el; + struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data); + + /* 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; + + el = ldb_msg_find_element(kd->subkeys[idx], "key"); + SMB_ASSERT(el != NULL); + SMB_ASSERT(el->num_values != 0); + + if (name != NULL) + *name = talloc_strdup(mem_ctx, (char *)el->values[0].data); + + if (classname != NULL) + *classname = NULL; /* TODO: Store properly */ + + if (last_mod_time != NULL) + *last_mod_time = 0; /* TODO: we need to add this to the + ldb backend properly */ + + return WERR_OK; +} + +static WERROR ldb_get_value_by_id(TALLOC_CTX *mem_ctx, const struct hive_key *k, int idx, + const char **name, uint32_t *data_type, DATA_BLOB *data) +{ + struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data); + + /* 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); + struct ldb_context *c = kd->ldb; + struct ldb_result *res; + int ret; + char *query = talloc_asprintf(mem_ctx, "(value=%s)", name); + + ret = ldb_search(c, kd->dn, LDB_SCOPE_ONELEVEL, query, NULL, &res); + + talloc_free(query); + + 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; + } + + if (res->count == 0) + return WERR_NOT_FOUND; + + reg_ldb_unpack_value(mem_ctx, res->msgs[0], NULL, data_type, data); + + return WERR_OK; +} + +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 *ldap_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; + + ldap_path = reg_path_to_ldb(mem_ctx, h, name, NULL); + + ret = ldb_search(c, ldap_path, LDB_SCOPE_BASE, "(key=*)", NULL, &res); + + if (ret != LDB_SUCCESS) { + DEBUG(0, ("Error opening key '%s': %s\n", + ldb_dn_get_linearized(ldap_path), ldb_errstring(c))); + return WERR_FOOBAR; + } else if (res->count == 0) { + DEBUG(0, ("Key '%s' not found\n", ldb_dn_get_linearized(ldap_path))); + talloc_free(res); + return WERR_NOT_FOUND; + } + + newkd = talloc_zero(mem_ctx, struct ldb_key_data); + newkd->key.ops = ®_backend_ldb; + newkd->ldb = talloc_reference(newkd, kd->ldb); + newkd->dn = ldb_dn_copy(mem_ctx, res->msgs[0]->dn); + + *key = (struct hive_key *)newkd; + + talloc_free(res); + + 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 hive_key **k) +{ + struct ldb_key_data *kd; + struct ldb_context *wrap; + + if (location == NULL) + return WERR_INVALID_PARAM; + + wrap = ldb_wrap_connect(parent_ctx, location, session_info, + credentials, 0, NULL); + + if (wrap == NULL) { + DEBUG(1, (__FILE__": unable to connect\n")); + return WERR_FOOBAR; + } + + 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) +{ + const struct ldb_key_data *parentkd = (const struct ldb_key_data *)parent; + struct ldb_message *msg; + struct ldb_key_data *newkd; + int ret; + + msg = ldb_msg_new(mem_ctx); + + msg->dn = reg_path_to_ldb(msg, parent, name, NULL); + + ldb_msg_add_string(msg, "key", talloc_strdup(mem_ctx, name)); + if (classname != NULL) + ldb_msg_add_string(msg, "classname", talloc_strdup(mem_ctx, classname)); + + ret = ldb_add(parentkd->ldb, msg); + if (ret < 0) { + DEBUG(1, ("ldb_msg_add: %s\n", ldb_errstring(parentkd->ldb))); + return WERR_FOOBAR; + } + + DEBUG(2, ("key added: %s\n", ldb_dn_get_linearized(msg->dn))); + + newkd = talloc_zero(mem_ctx, struct ldb_key_data); + newkd->ldb = talloc_reference(newkd, parentkd->ldb); + newkd->key.ops = ®_backend_ldb; + newkd->dn = talloc_steal(newkd, msg->dn); + + *newkey = (struct hive_key *)newkd; + + return WERR_OK; +} + +static WERROR ldb_del_key (const struct hive_key *key, const char *child) +{ + int ret; + struct ldb_key_data *parentkd = talloc_get_type(key, struct ldb_key_data); + struct ldb_dn *childdn; + + childdn = ldb_dn_copy(parentkd->ldb, parentkd->dn); + ldb_dn_add_child_fmt(childdn, "key=%s", child); + + ret = ldb_delete(parentkd->ldb, childdn); + + talloc_free(childdn); + + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + return WERR_NOT_FOUND; + } else if (ret < 0) { + DEBUG(1, ("ldb_del_key: %s\n", ldb_errstring(parentkd->ldb))); + return WERR_FOOBAR; + } + + return WERR_OK; +} + +static WERROR ldb_del_value (struct hive_key *key, const char *child) +{ + int ret; + struct ldb_key_data *kd = talloc_get_type(key, struct ldb_key_data); + struct ldb_dn *childdn; + + childdn = ldb_dn_copy(kd->ldb, kd->dn); + ldb_dn_add_child_fmt(childdn, "value=%s", child); + + ret = ldb_delete(kd->ldb, childdn); + + talloc_free(childdn); + + if (ret == LDB_ERR_NO_SUCH_OBJECT) { + return WERR_NOT_FOUND; + } else if (ret < 0) { + DEBUG(1, ("ldb_del_value: %s\n", ldb_errstring(kd->ldb))); + return WERR_FOOBAR; + } + + 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); + int ret; + TALLOC_CTX *mem_ctx = talloc_init("ldb_set_value"); + + msg = reg_ldb_pack_value(kd->ldb, mem_ctx, name, type, data); + + msg->dn = ldb_dn_copy(msg, kd->dn); + ldb_dn_add_child_fmt(msg->dn, "value=%s", name); + + ret = ldb_add(kd->ldb, msg); + if (ret < 0) { + ret = ldb_modify(kd->ldb, msg); + if (ret < 0) { + DEBUG(1, ("ldb_msg_add: %s\n", ldb_errstring(kd->ldb))); + talloc_free(mem_ctx); + return WERR_FOOBAR; + } + } + + 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) +{ + struct ldb_key_data *kd = talloc_get_type(key, struct ldb_key_data); + + /* FIXME */ + if (classname != NULL) + *classname = NULL; + + if (num_subkeys != NULL) { + W_ERROR_NOT_OK_RETURN(cache_subkeys(kd)); + *num_subkeys = kd->subkey_count; + } + + if (num_values != NULL) { + W_ERROR_NOT_OK_RETURN(cache_values(kd)); + *num_values = kd->value_count; + } + + if (last_change_time != NULL) + *last_change_time = 0; + + 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 0000000000..aefb11bde2 --- /dev/null +++ b/source4/lib/registry/local.c @@ -0,0 +1,333 @@ +/* + 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 2 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" +#include "build.h" + +struct reg_key_path { + uint32_t predefined_key; + const char **elements; +}; + +struct registry_local { + struct registry_context registry; + + struct mountpoint { + struct reg_key_path path; + struct hive_key *key; + struct mountpoint *prev, *next; + } *mountpoints; + + struct auth_session_info *session_info; + struct cli_credentials *credentials; +}; + +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); + local_key->hive_key = talloc_steal(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 = talloc_strdup(mem_ctx, path), + *curbegin = orig, + *curend = strchr(orig, '\\'); + 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 (local_parent->path.elements != NULL) { + elements = talloc_array(mem_ctx, const char *, + str_list_length(local_parent->path.elements) + 1); + 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; + } + + while (curbegin != NULL && *curbegin) { + if (curend != NULL) + *curend = '\0'; + elements = talloc_realloc(mem_ctx, elements, const char *, el+2); + elements[el] = talloc_strdup(elements, curbegin); + 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, '\\'); + } + 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 (const 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_NOT_FOUND; + + *key = reg_import_hive_key(ctx, mp->key, + mp->path.predefined_key, + mp->path.elements + ); + + return WERR_OK; +} + +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_key, + const char *name, + const char *key_class, + struct security_descriptor *security, + struct registry_key **key) +{ + const struct local_key *local_parent; + struct hive_key *hivekey; + const char **elements; + int i; + char *last_part; + + last_part = strrchr(name, '\\'); + if (last_part == NULL) { + last_part = name; + local_parent = (const struct local_key *)parent_key; + } else { + W_ERROR_NOT_OK_RETURN(reg_open_key(mem_ctx, parent_key, + talloc_strndup(mem_ctx, name, last_part-name), + &local_parent)); + last_part++; + } + + W_ERROR_NOT_OK_RETURN(hive_key_add_name(mem_ctx, local_parent->hive_key, + last_part, key_class, security, &hivekey)); + + if (local_parent->path.elements != NULL) { + elements = talloc_array(hivekey, const char *, + str_list_length(local_parent->path.elements)+2); + for (i = 0; local_parent->path.elements[i] != NULL; i++) { + elements[i] = talloc_reference(elements, + local_parent->path.elements[i]); + } + } else { + elements = talloc_array(hivekey, const char *, 2); + i = 0; + } + + elements[i] = talloc_strdup(elements, name); + elements[i+1] = NULL; + + *key = reg_import_hive_key(local_parent->global.context, hivekey, + local_parent->path.predefined_key, + 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; + + return hive_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; + + 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 (struct registry_key *key, const char *name) +{ + const struct local_key *local = (const struct local_key *)key; + + return hive_key_del(local->hive_key, name); +} + +static WERROR local_delete_value (struct registry_key *key, const char *name) +{ + const struct local_key *local = (const struct local_key *)key; + + return hive_del_value(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) +{ + 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); +} + +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, +}; + +WERROR reg_open_local(TALLOC_CTX *mem_ctx, struct registry_context **ctx, + struct auth_session_info *session_info, + struct cli_credentials *credentials) +{ + struct registry_local *ret = talloc_zero(mem_ctx, struct registry_local); + + W_ERROR_HAVE_NO_MEMORY(ret); + + ret->registry.ops = &local_ops; + ret->session_info = session_info; + ret->credentials = credentials; + + *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 = talloc(rctx, struct mountpoint); + int i = 0; + + mp->path.predefined_key = key_id; + mp->prev = mp->next = NULL; + mp->key = hive_key; + if (elements != NULL) { + mp->path.elements = talloc_array(mp, const char *, + str_list_length(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 index 0e237bfece..7bcaa1502c 100644 --- a/source4/lib/registry/man/regdiff.1.xml +++ b/source4/lib/registry/man/regdiff.1.xml @@ -54,7 +54,7 @@ <varlistentry> <term>--backend BACKEND</term> <listitem><para>Name of backend to load. Possible values are: - w95, nt4, gconf, dir and rpc. The default is <emphasis>dir</emphasis>. + creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>. </para> <para> This argument can be specified twice: once for the first diff --git a/source4/lib/registry/man/regpatch.1.xml b/source4/lib/registry/man/regpatch.1.xml index c04bad9e66..d9dcdcbf80 100644 --- a/source4/lib/registry/man/regpatch.1.xml +++ b/source4/lib/registry/man/regpatch.1.xml @@ -49,7 +49,7 @@ <varlistentry> <term>--backend BACKEND</term> <listitem><para>Name of backend to load. Possible values are: - w95, nt4, gconf, dir and rpc. The default is <emphasis>dir</emphasis>. + creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>. </para></listitem> </varlistentry> diff --git a/source4/lib/registry/man/regshell.1.xml b/source4/lib/registry/man/regshell.1.xml index edec729120..9f16d8cc24 100644 --- a/source4/lib/registry/man/regshell.1.xml +++ b/source4/lib/registry/man/regshell.1.xml @@ -48,7 +48,7 @@ <varlistentry> <term>--backend BACKEND</term> <listitem><para>Name of backend to load. Possible values are: - w95, nt4, gconf, dir and rpc. The default is <emphasis>dir</emphasis>. + creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>. </para></listitem> </varlistentry> diff --git a/source4/lib/registry/man/regtree.1.xml b/source4/lib/registry/man/regtree.1.xml index aa31855a2b..93f15e1fb2 100644 --- a/source4/lib/registry/man/regtree.1.xml +++ b/source4/lib/registry/man/regtree.1.xml @@ -48,7 +48,7 @@ <varlistentry> <term>--backend BACKEND</term> <listitem><para>Name of backend to load. Possible values are: - w95, nt4, gconf, dir and rpc. The default is <emphasis>dir</emphasis>. + creg, regf, dir and rpc. The default is <emphasis>dir</emphasis>. </para></listitem> </varlistentry> diff --git a/source4/lib/registry/patchfile.c b/source4/lib/registry/patchfile.c index 12847eedd8..50c8f54397 100644 --- a/source4/lib/registry/patchfile.c +++ b/source4/lib/registry/patchfile.c @@ -1,8 +1,9 @@ /* Unix SMB/CIFS implementation. - Reading .REG files + Reading registry patch files - Copyright (C) Jelmer Vernooij 2004 + 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 @@ -19,153 +20,191 @@ */ #include "includes.h" +#include "lib/registry/patchfile.h" #include "lib/registry/registry.h" #include "system/filesys.h" -/** - * @file - * @brief Registry patch files - */ - -#define DEFAULT_IDENT_STRING "SAMBA4 REGISTRY" - -static struct reg_diff_key *diff_find_add_key(struct reg_diff *diff, const char *path) -{ - int i; - - for (i = 0; diff->numkeys; i++) { - if (!strcasecmp(diff->keys[i].name, path)) - return &diff->keys[i]; - } - diff->keys = talloc_realloc(diff, diff->keys, struct reg_diff_key, diff->numkeys+2); - diff->keys[diff->numkeys].name = talloc_strdup(diff->keys, path); - diff->keys[diff->numkeys].changetype = REG_DIFF_CHANGE_KEY; - diff->keys[diff->numkeys].numvalues = 0; - diff->keys[diff->numkeys].values = NULL; +_PUBLIC_ WERROR reg_preg_diff_load(int fd, const struct reg_diff_callbacks *callbacks, void *callback_data); - diff->numkeys++; - return NULL; -} +_PUBLIC_ WERROR reg_dotreg_diff_load(int fd, const struct reg_diff_callbacks *callbacks, void *callback_data); /* * Generate difference between two keys */ -static WERROR reg_generate_diff_key(struct reg_diff *diff, struct registry_key *oldkey, struct registry_key *newkey) +WERROR reg_generate_diff_key(struct registry_key *oldkey, + struct registry_key *newkey, + const char *path, + const struct reg_diff_callbacks *callbacks, + void *callback_data) { int i; struct registry_key *t1, *t2; - struct registry_value *v1, *v2; - WERROR error1, error2; + 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; - /* Subkeys that were deleted */ - for(i = 0; W_ERROR_IS_OK(error1 = reg_key_get_subkey_by_index(mem_ctx, oldkey, i, &t1)); i++) { - error2 = reg_key_get_subkey_by_name(mem_ctx, newkey, t1->name, &t2); + if (oldkey != NULL) { + error = reg_key_get_info(mem_ctx, oldkey, NULL, &old_num_subkeys, &old_num_values, + NULL); + if (!W_ERROR_IS_OK(error)) { + DEBUG(0, ("Error occured while getting key info: %s\n", + win_errstr(error))); + return error; + } + } else { + old_num_subkeys = 0; + old_num_values = 0; + } - if (W_ERROR_IS_OK(error2)) + /* Subkeys that were deleted */ + for (i = 0; i < old_num_subkeys; i++) { + error1 = reg_key_get_subkey_by_index(mem_ctx, oldkey, i, &keyname1, + NULL, NULL); + if (!W_ERROR_IS_OK(error1)) { + DEBUG(0, ("Error occured while getting subkey by index: %s\n", + win_errstr(error2))); continue; + } + + if (newkey != NULL) { + error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2); + + if (W_ERROR_IS_OK(error2)) + continue; + } else { + error2 = WERR_DEST_NOT_FOUND; + t2 = NULL; + } if (!W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) { - DEBUG(0, ("Error occured while getting subkey by name: %d\n", W_ERROR_V(error2))); + DEBUG(0, ("Error occured while getting subkey by name: %s\n", + win_errstr(error2))); + talloc_free(mem_ctx); return error2; } /* newkey didn't have such a subkey, add del diff */ - diff->keys = talloc_realloc(diff, diff->keys, struct reg_diff_key, diff->numkeys+2); - diff->keys[diff->numkeys].name = talloc_strdup(diff->keys, t1->path); - diff->keys[diff->numkeys].changetype = REG_DIFF_DEL_KEY; - diff->numkeys++; + tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1); + callbacks->del_key(callback_data, tmppath); + talloc_free(tmppath); } - if(!W_ERROR_EQUAL(error1, WERR_NO_MORE_ITEMS)) { - DEBUG(0, ("Error occured while getting subkey by index: %d\n", W_ERROR_V(error1))); - talloc_free(mem_ctx); - return error1; + if (newkey != NULL) { + error = reg_key_get_info(mem_ctx, newkey, NULL, &new_num_subkeys, &new_num_values, + NULL); + if (!W_ERROR_IS_OK(error)) { + DEBUG(0, ("Error occured while getting key info: %s\n", + win_errstr(error))); + return error; + } + } else { + new_num_subkeys = 0; + new_num_values = 0; } /* Subkeys that were added */ - for(i = 0; W_ERROR_IS_OK(error1 = reg_key_get_subkey_by_index(mem_ctx, newkey, i, &t1)); i++) { - error2 = reg_key_get_subkey_by_name(mem_ctx, oldkey, t1->name, &t2); + for(i = 0; i < new_num_subkeys; i++) { + error1 = reg_key_get_subkey_by_index(mem_ctx, newkey, i, &keyname1, + NULL, NULL); + if (!W_ERROR_IS_OK(error1)) { + DEBUG(0, ("Error occured while getting subkey by index: %s\n", + win_errstr(error1))); + talloc_free(mem_ctx); + return error1; + } + + if (oldkey != NULL) { + error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1); + + if (W_ERROR_IS_OK(error2)) + continue; + } else { + t1 = NULL; + error2 = WERR_DEST_NOT_FOUND; + } - if (W_ERROR_IS_OK(error2)) - continue; - if (!W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) { - DEBUG(0, ("Error occured while getting subkey by name: %d\n", W_ERROR_V(error2))); + DEBUG(0, ("Error occured while getting subkey by name: %s\n", + win_errstr(error2))); + talloc_free(mem_ctx); return error2; } /* oldkey didn't have such a subkey, add add diff */ - diff->keys = talloc_realloc(diff, diff->keys, struct reg_diff_key, diff->numkeys+2); - diff->keys[diff->numkeys].name = talloc_strdup(diff->keys, t1->path); - diff->keys[diff->numkeys].changetype = REG_DIFF_CHANGE_KEY; - diff->keys[diff->numkeys].numvalues = 0; - diff->keys[diff->numkeys].values = NULL; - diff->numkeys++; - - reg_generate_diff_key(diff, t1, t2); - } + tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1); + callbacks->add_key(callback_data, tmppath); - if(!W_ERROR_EQUAL(error1, WERR_NO_MORE_ITEMS)) { - DEBUG(0, ("Error occured while getting subkey by index: %d\n", W_ERROR_V(error1))); - talloc_free(mem_ctx); - return error1; + W_ERROR_NOT_OK_RETURN( + reg_open_key(mem_ctx, newkey, keyname1, &t2)); + + reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data); + talloc_free(tmppath); } /* Values that were changed */ - for(i = 0; W_ERROR_IS_OK(error1 = reg_key_get_value_by_index(mem_ctx, newkey, i, &v1)); i++) { - struct reg_diff_key *thiskey = NULL; - error2 = reg_key_get_value_by_name(mem_ctx, oldkey, v1->name, &v2); + for(i = 0; i < new_num_values; i++) { + const char *name; + uint32_t type1, type2; + DATA_BLOB contents1, contents2; + + error1 = reg_key_get_value_by_index(mem_ctx, newkey, i, + &name, &type1, &contents1); + if (!W_ERROR_IS_OK(error1)) { + DEBUG(0, ("Unable to get key by index: %s\n", + win_errstr(error1))); + talloc_free(mem_ctx); + return error1; + } + + if (oldkey != NULL) { + error2 = reg_key_get_value_by_name(mem_ctx, oldkey, name, + &type2, &contents2); + } else + error2 = WERR_DEST_NOT_FOUND; if(!W_ERROR_IS_OK(error2) && !W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) { - DEBUG(0, ("Error occured while getting value by name: %d\n", W_ERROR_V(error2))); + DEBUG(0, ("Error occured while getting value by name: %s\n", + win_errstr(error2))); + talloc_free(mem_ctx); return error2; } - if (W_ERROR_IS_OK(error2) && data_blob_cmp(&v1->data, &v2->data) == 0) + if (W_ERROR_IS_OK(error2) && data_blob_cmp(&contents1, &contents2) == 0) continue; - thiskey = diff_find_add_key(diff, oldkey->path); - thiskey->values = talloc_realloc(diff, thiskey->values, struct reg_diff_value, thiskey->numvalues+2); - thiskey->values[thiskey->numvalues].name = talloc_strdup(thiskey->values, v1->name); - thiskey->values[thiskey->numvalues].type = v2->data_type; - thiskey->values[thiskey->numvalues].changetype = REG_DIFF_SET_VAL; - thiskey->values[thiskey->numvalues].data = data_blob_dup_talloc(thiskey->values, &v2->data); - thiskey->numvalues++; - } - - if(!W_ERROR_EQUAL(error1, WERR_NO_MORE_ITEMS)) { - DEBUG(0, ("Error occured while getting value by index: %d\n", W_ERROR_V(error1))); - talloc_free(mem_ctx); - return error1; + callbacks->set_value(callback_data, path, name, type1, contents1); } /* Values that were deleted */ - for(i = 0; W_ERROR_IS_OK(error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &v1)); i++) { - struct reg_diff_key *thiskey = NULL; - error2 = reg_key_get_value_by_name(mem_ctx, newkey, v1->name, &v2); + for (i = 0; i < old_num_values; i++) { + const char *name; + error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &name, + NULL, NULL); + if (!W_ERROR_IS_OK(error1)) { + DEBUG(0, ("Error ocurred getting value by index: %s\n", + win_errstr(error1))); + talloc_free(mem_ctx); + return error1; + } + + error2 = reg_key_get_value_by_name(mem_ctx, newkey, name, NULL, + NULL); if (W_ERROR_IS_OK(error2)) continue; if (!W_ERROR_EQUAL(error2, WERR_DEST_NOT_FOUND)) { - DEBUG(0, ("Error occured while getting value by name: %d\n", W_ERROR_V(error2))); + DEBUG(0, ("Error occured while getting value by name: %s\n", + win_errstr(error2))); return error2; } - thiskey = diff_find_add_key(diff, oldkey->path); - thiskey->values = talloc_realloc(diff, thiskey->values, struct reg_diff_value, thiskey->numvalues+2); - thiskey->values[thiskey->numvalues].name = talloc_strdup(thiskey->values, v1->name); - thiskey->values[thiskey->numvalues].changetype = REG_DIFF_DEL_VAL; - thiskey->numvalues++; - } - - if(!W_ERROR_EQUAL(error1, WERR_NO_MORE_ITEMS)) { - DEBUG(0, ("Error occured while getting value by index: %d\n", W_ERROR_V(error1))); - talloc_free(mem_ctx); - return error1; + callbacks->del_value(callback_data, path, name); } talloc_free(mem_ctx); @@ -175,244 +214,206 @@ static WERROR reg_generate_diff_key(struct reg_diff *diff, struct registry_key * /** * Generate diff between two registry contexts */ -_PUBLIC_ struct reg_diff *reg_generate_diff(TALLOC_CTX *mem_ctx, struct registry_context *ctx1, struct registry_context *ctx2) +_PUBLIC_ WERROR reg_generate_diff(struct registry_context *ctx1, + struct registry_context *ctx2, + const struct reg_diff_callbacks *callbacks, + void *callback_data) { - struct reg_diff *diff = talloc_zero(mem_ctx, struct reg_diff); int i; WERROR error; - for(i = HKEY_CLASSES_ROOT; i <= HKEY_PERFORMANCE_NLSTEXT; i++) { - struct registry_key *r1, *r2; + for(i = HKEY_FIRST; i <= HKEY_LAST; i++) { + struct registry_key *r1 = NULL, *r2 = NULL; error = reg_get_predefined_key(ctx1, i, &r1); - if (!W_ERROR_IS_OK(error)) { + if (!W_ERROR_IS_OK(error) && !W_ERROR_EQUAL(error, WERR_NOT_FOUND)) { DEBUG(0, ("Unable to open hive %s for backend 1\n", reg_get_predef_name(i))); - continue; } error = reg_get_predefined_key(ctx2, i, &r2); - if (!W_ERROR_IS_OK(error)) { + if (!W_ERROR_IS_OK(error) && !W_ERROR_EQUAL(error, WERR_NOT_FOUND)) { DEBUG(0, ("Unable to open hive %s for backend 2\n", reg_get_predef_name(i))); - continue; } - reg_generate_diff_key(diff, r1, r2); - } - - return diff; -} - -/** - * Save registry diff - */ -_PUBLIC_ WERROR reg_diff_save(const struct reg_diff *diff, const char *filename) -{ - int xf, i, j; - - if (filename) { - xf = open(filename, O_CREAT, 0755); - if (xf == -1) { - DEBUG(0, ("Unable to open %s\n", filename)); - return WERR_BADFILE; - } - } else - xf = STDIN_FILENO; - - fdprintf(xf, "%s\n\n", diff->format?diff->format:DEFAULT_IDENT_STRING); - - for (i = 0; i < diff->numkeys; i++) { - if (diff->keys[i].changetype == REG_DIFF_DEL_KEY) { - fdprintf(xf, "-%s\n\n", diff->keys[i].name); + if (r1 == NULL && r2 == NULL) continue; - } - fdprintf(xf, "[%s]\n", diff->keys[i].name); - - for (j = 0; j < diff->keys[i].numvalues; j++) { - fdprintf(xf, "\"%s\"=", diff->keys[i].values[j].name); - switch (diff->keys[i].values[j].changetype) { - case REG_DIFF_DEL_VAL: - fdprintf(xf, "-\n"); - break; - case REG_DIFF_SET_VAL: - fdprintf(xf, "%s:%s\n", - str_regtype(diff->keys[i].values[j].type), - reg_val_data_string(NULL, - diff->keys[i].values[j].type, - &diff->keys[i].values[j].data)); - break; - } + error = reg_generate_diff_key(r1, r2, reg_get_predef_name(i), callbacks, callback_data); + if (!W_ERROR_IS_OK(error)) { + DEBUG(0, ("Unable to determine diff: %s\n", win_errstr(error))); + return error; } - - fdprintf(xf, "\n"); } - - close(xf); - + if (callbacks->done != NULL) { + callbacks->done(callback_data); + } return WERR_OK; } /** * Load diff file */ -_PUBLIC_ struct reg_diff *reg_diff_load(TALLOC_CTX *ctx, const char *fn) +_PUBLIC_ WERROR reg_diff_load(const char *filename, const struct reg_diff_callbacks *callbacks, void *callback_data) { - struct reg_diff *diff; int fd; - char *line, *p, *q; - struct reg_diff_key *curkey = NULL; - struct reg_diff_value *curval; - - fd = open(fn, O_RDONLY, 0); + char hdr[4]; + + fd = open(filename, O_RDONLY, 0); if (fd == -1) { - DEBUG(0, ("Error opening registry patch file `%s'\n", fn)); - return NULL; + DEBUG(0, ("Error opening registry patch file `%s'\n", filename)); + return WERR_GENERAL_FAILURE; } - diff = talloc_zero(ctx, struct reg_diff); - if (diff == NULL) { - close(fd); - return NULL; + if (read(fd, &hdr, 4) != 4) { + DEBUG(0, ("Error reading registry patch file `%s'\n", filename)); + return WERR_GENERAL_FAILURE; } - - diff->format = afdgets(fd, diff, 0); - if (!diff->format) { - talloc_free(diff); - close(fd); - return NULL; + + /* Reset position in file */ + lseek(fd, 0, SEEK_SET); +#if 0 + 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); } +} - while ((line = afdgets(fd, diff, 0))) { - /* Ignore comments and empty lines */ - if (strlen(line) == 0 || line[0] == ';') { - curkey = NULL; - talloc_free(line); - continue; - } +/** + * The reg_diff_apply functions + */ +static WERROR reg_diff_apply_add_key(void *_ctx, const char *key_name) +{ + struct registry_context *ctx = _ctx; + struct registry_key *tmp; + WERROR error; - /* Start of key */ - if (line[0] == '[') { - p = strchr_m(line, ']'); - if (p[strlen(p)-2] != ']') { - DEBUG(0, ("Malformed line\n")); - return NULL; - } - diff->keys = talloc_realloc(diff, diff->keys, struct reg_diff_key, diff->numkeys+2); - diff->keys[diff->numkeys].name = talloc_strndup(diff->keys, line+1, strlen(line)-2); - diff->keys[diff->numkeys].changetype = REG_DIFF_CHANGE_KEY; - diff->keys[diff->numkeys].numvalues = 0; - diff->keys[diff->numkeys].values = NULL; - curkey = &diff->keys[diff->numkeys]; - diff->numkeys++; - talloc_free(line); - continue; - } + error = reg_key_add_abs(ctx, ctx, key_name, 0, NULL, &tmp); - /* Deleting key */ - if (line[0] == '-') { - diff->keys = talloc_realloc(diff, diff->keys, struct reg_diff_key, diff->numkeys+2); - diff->keys[diff->numkeys].name = talloc_strdup(diff->keys, line+1); - diff->keys[diff->numkeys].changetype = REG_DIFF_DEL_KEY; - diff->numkeys++; - talloc_free(line); - continue; - } + if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) && !W_ERROR_IS_OK(error)) { + DEBUG(0, ("Error adding new key '%s': %s\n", key_name, win_errstr(error))); + return error; + } + return WERR_OK; +} - /* Deleting/Changing value */ - p = strchr_m(line, '='); - if (p == NULL) { - DEBUG(0, ("Malformed line\n")); - talloc_free(line); - continue; - } +static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name) +{ + struct registry_context *ctx = _ctx; + WERROR error; - *p = '\0'; p++; + error = reg_key_del_abs(ctx, key_name); - if (curkey == NULL) { - DEBUG(0, ("Value change without key\n")); - talloc_free(line); - continue; - } + if(!W_ERROR_IS_OK(error)) { + DEBUG(0, ("Unable to delete key '%s'\n", key_name)); + return error; + } + + return WERR_OK; +} - curkey->values = talloc_realloc(diff->keys, curkey->values, struct reg_diff_value, curkey->numvalues+2); - curval = &curkey->values[curkey->numvalues]; - curkey->numvalues++; - curval->name = talloc_strdup(curkey->values, line); +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 = _ctx; + struct registry_key *tmp; + WERROR error; + + /* Open key */ + error = reg_open_key_abs(ctx, ctx, path, &tmp); - /* Delete value */ - if (strcmp(p, "-")) { - curval->changetype = REG_DIFF_DEL_VAL; - talloc_free(line); - continue; - } - - q = strchr_m(p, ':'); - if (q) { - *q = '\0'; - q++; - } + if (W_ERROR_EQUAL(error, WERR_DEST_NOT_FOUND)) { + DEBUG(0, ("Error opening key '%s'\n", path)); + return error; + } - curval->changetype = REG_DIFF_SET_VAL; - reg_string_to_val(curkey->values, q?p:"REG_SZ", q?q:p, &curval->type, &curval->data); + /* Set value */ + error = reg_val_set(tmp, value_name, + value_type, value); + if (!W_ERROR_IS_OK(error)) { + DEBUG(0, ("Error setting value '%s'\n", value_name)); + return error; + } + + return WERR_OK; +} - talloc_free(line); +static WERROR reg_diff_apply_del_value (void *_ctx, const char *key_name, const char *value_name) +{ + struct registry_context *ctx = _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; } - close(fd); + error = reg_del_value(tmp, value_name); + if (!W_ERROR_IS_OK(error)) { + DEBUG(0, ("Error deleting value '%s'\n", value_name)); + return error; + } + - return diff; + return WERR_OK; } -/** - * Apply diff to a registry context - */ -_PUBLIC_ BOOL reg_diff_apply (const struct reg_diff *diff, struct registry_context *ctx) +static WERROR reg_diff_apply_del_all_values(void *_ctx, const char *key_name) { - TALLOC_CTX *mem_ctx = talloc_init("apply_cmd_file"); - struct registry_key *tmp = NULL; + struct registry_context *ctx = _ctx; + struct registry_key *key; WERROR error; - int i, j; + int i; + uint32_t num_values; - for (i = 0; i < diff->numkeys; i++) { - if (diff->keys[i].changetype == REG_DIFF_DEL_KEY) { - error = reg_key_del_abs(ctx, diff->keys[i].name); + error = reg_open_key_abs(ctx, ctx, key_name, &key); - if(!W_ERROR_IS_OK(error)) { - DEBUG(0, ("Unable to delete key '%s'\n", diff->keys[i].name)); - return False; - } + if (!W_ERROR_IS_OK(error)) { + DEBUG(0, ("Error opening key '%s'\n", key_name)); + return error; + } - continue; - } + W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx, key, + NULL, + NULL, + &num_values, + NULL)); + + for (i = 0; i < num_values; i++) { + const char *name; + W_ERROR_NOT_OK_RETURN(reg_key_get_value_by_index(ctx, key, i, &name, + NULL, NULL)); + W_ERROR_NOT_OK_RETURN(reg_del_value(key, name)); + } - /* Add / change key */ - error = reg_open_key_abs(mem_ctx, ctx, diff->keys[i].name, &tmp); + return WERR_OK; +} - /* If we found it, apply the other bits, else create such a key */ - if (W_ERROR_EQUAL(error, WERR_DEST_NOT_FOUND)) { - if(!W_ERROR_IS_OK(reg_key_add_abs(mem_ctx, ctx, diff->keys[i].name, 0, NULL, &tmp))) { - DEBUG(0, ("Error adding new key '%s'\n", diff->keys[i].name)); - return False; - } - } +/** + * Apply diff to a registry context + */ +_PUBLIC_ WERROR reg_diff_apply (const char *filename, struct registry_context *ctx) +{ + struct reg_diff_callbacks callbacks; - for (j = 0; j < diff->keys[i].numvalues; j++) { - if (diff->keys[i].values[j].changetype == REG_DIFF_DEL_VAL) { - error = reg_del_value(tmp, diff->keys[i].values[j].name); - if (!W_ERROR_IS_OK(error)) { - DEBUG(0, ("Error deleting value '%s'\n", diff->keys[i].values[j].name)); - return False; - } - - error = reg_val_set(tmp, diff->keys[i].values[j].name, - diff->keys[i].values[j].type, - diff->keys[i].values[j].data); - if (!W_ERROR_IS_OK(error)) { - DEBUG(0, ("Error setting value '%s'\n", diff->keys[i].values[j].name)); - return False; - } - } - } - } + 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 True; + return reg_diff_load(filename, &callbacks, ctx); } diff --git a/source4/lib/registry/patchfile.h b/source4/lib/registry/patchfile.h new file mode 100644 index 0000000000..194e2a132a --- /dev/null +++ b/source4/lib/registry/patchfile.h @@ -0,0 +1,52 @@ +/* + Unix SMB/CIFS implementation. + Patchfile interface + Copyright (C) Jelmer Vernooij 2006 + 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 2 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. +*/ + +#ifndef _PATCHFILE_H +#define _PATCHFILE_H + +#include "lib/registry/registry.h" + +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); +}; + +_PUBLIC_ WERROR reg_diff_apply (const char *filename, + struct registry_context *ctx); + +_PUBLIC_ WERROR reg_generate_diff(struct registry_context *ctx1, + struct registry_context *ctx2, + const struct reg_diff_callbacks *callbacks, + void *callback_data); +_PUBLIC_ WERROR reg_dotreg_diff_save(TALLOC_CTX *ctx, const char *filename, + struct reg_diff_callbacks **callbacks, void **callback_data); +_PUBLIC_ 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); + +#endif /* _PATCHFILE_H */ diff --git a/source4/lib/registry/patchfile_dotreg.c b/source4/lib/registry/patchfile_dotreg.c new file mode 100644 index 0000000000..f11ceb1be0 --- /dev/null +++ b/source4/lib/registry/patchfile_dotreg.c @@ -0,0 +1,247 @@ +/* + Unix SMB/CIFS implementation. + Reading .REG files + + Copyright (C) Jelmer Vernooij 2004-2007 + Copyright (C) Wilco Baan Hofman 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 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 UTF-16 */ + +#include "includes.h" +#include "lib/registry/patchfile.h" +#include "lib/registry/registry.h" +#include "system/filesys.h" + +/** + * @file + * @brief Registry patch files + */ + +#define HEADER_STRING "REGEDIT4" + +struct dotreg_data { + int fd; +}; + +static WERROR reg_dotreg_diff_add_key(void *_data, const char *key_name) +{ + struct dotreg_data *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 = _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 = _data; + + fdprintf(data->fd, "\"%s\"=%s:%s\n", + value_name, str_regtype(value_type), + reg_val_data_string(NULL, value_type, value)); + + return WERR_OK; +} + +static WERROR reg_dotreg_diff_del_value(void *_data, const char *path, const char *value_name) +{ + struct dotreg_data *data = _data; + + fdprintf(data->fd, "\"%s\"=-\n", value_name); + + return WERR_OK; +} + +static WERROR reg_dotreg_diff_done(void *_data) +{ + struct dotreg_data *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, 0755); + if (data->fd == -1) { + DEBUG(0, ("Unable to open %s\n", filename)); + return WERR_BADFILE; + } + } else { + data->fd = STDOUT_FILENO; + } + + fdprintf(data->fd, "%s\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 value; + + line = afdgets(fd, mem_ctx, 0); + if (!line) { + DEBUG(0, ("Can't read from file.\n")); + talloc_free(mem_ctx); + close(fd); + return WERR_GENERAL_FAILURE; + } + + while ((line = afdgets(fd, mem_ctx, 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] == '[') { + p = strchr_m(line, ']'); + if (p[strlen(p)-1] != ']') { + DEBUG(0, ("Missing ']'\n")); + return WERR_GENERAL_FAILURE; + } + /* Deleting key */ + if (line[1] == '-') { + curkey = talloc_strndup(line, line+2, strlen(line)-3); + + 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); + + 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 */ + p = strchr_m(line, '='); + if (p == NULL) { + DEBUG(0, ("Malformed line\n")); + talloc_free(line); + continue; + } + + *p = '\0'; p++; + + if (curkey == NULL) { + DEBUG(0, ("Value change without key\n")); + talloc_free(line); + continue; + } + + /* Delete value */ + if (strcmp(p, "-")) { + error = callbacks->del_value(callback_data, curkey, line); + if (!W_ERROR_IS_OK(error)) { + DEBUG(0, ("Error deleting value %s in key %s\n", line, curkey)); + talloc_free(mem_ctx); + return error; + } + + talloc_free(line); + continue; + } + + q = strchr_m(p, ':'); + if (q) { + *q = '\0'; + q++; + } + + reg_string_to_val(line, q?p:"REG_SZ", q?q:p, &value_type, &value); + + error = callbacks->set_value(callback_data, curkey, line, value_type, value); + if (!W_ERROR_IS_OK(error)) { + DEBUG(0, ("Error setting value for %s in %s\n", line, curkey)); + talloc_free(mem_ctx); + return error; + } + + talloc_free(line); + } + + close(fd); + + return WERR_OK; +} diff --git a/source4/lib/registry/patchfile_preg.c b/source4/lib/registry/patchfile_preg.c new file mode 100644 index 0000000000..1c8d76538a --- /dev/null +++ b/source4/lib/registry/patchfile_preg.c @@ -0,0 +1,270 @@ +/* + Unix SMB/CIFS implementation. + Reading Registry.pol PReg registry files + + 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 2 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 "pstring.h" + +struct preg_data { + int fd; +}; + +static WERROR preg_read_utf16(int fd, char *c) +{ + uint16_t v; + + if (read(fd, &v, 2) < 2) { + return WERR_GENERAL_FAILURE; + } + push_codepoint(c, v); + return WERR_OK; +} + +/* FIXME These functions need to be implemented */ +static WERROR reg_preg_diff_add_key(void *_data, const char *key_name) +{ + struct preg_data *data = _data; + return WERR_OK; +} + +static WERROR reg_preg_diff_del_key(void *_data, const char *key_name) +{ + struct preg_data *data = _data; + 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 = _data; + return WERR_OK; +} + +static WERROR reg_preg_diff_del_value(void *_data, const char *key_name, const char *value_name) +{ + struct preg_data *data = _data; + return WERR_OK; +} + +static WERROR reg_preg_diff_del_all_values(void *_data, const char *key_name) +{ + struct preg_data *data = _data; + return WERR_OK; +} + +static WERROR reg_preg_diff_done(void *_data) +{ + struct preg_data *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, 0755); + if (data->fd == -1) { + DEBUG(0, ("Unable to open %s\n", filename)); + return WERR_BADFILE; + } + } else { + data->fd = STDOUT_FILENO; + } + snprintf(preg_header.hdr, 4, "PReg"); + SIVAL(&preg_header, 4, 1); + write(data->fd, (uint8_t *)&preg_header,8); + + *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; + pstring buf; + char *buf_ptr = buf; + TALLOC_CTX *mem_ctx = talloc_init("reg_preg_diff_load"); + + + /* Read first 8 bytes (the header) */ + if (read(fd, &preg_header, 8) != 8) { + DEBUG(0, ("Could not read PReg file: %s\n", + strerror(errno))); + close(fd); + return WERR_GENERAL_FAILURE; + } + if (strncmp(preg_header.hdr, "PReg", 4) != 0) { + DEBUG(0, ("This file is not a valid preg registry file\n")); + close(fd); + return WERR_GENERAL_FAILURE; + } + if (preg_header.version > 1) { + DEBUG(0, ("Warning: file format version is higher than expected.\n")); + } + + /* Read the entries */ + while(1) { + char *key, *value_name; + uint32_t value_type, length; + DATA_BLOB data; + + if (!W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr))) { + break; + } + if (*buf_ptr != '[') { + DEBUG(0, ("Error in PReg file.\n")); + close(fd); + return WERR_GENERAL_FAILURE; + } + + /* Get the path */ + buf_ptr = buf; + while (W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) && *buf_ptr != ';' && buf_ptr-buf < sizeof(buf)) { + buf_ptr++; + } + key = talloc_asprintf(mem_ctx, "\\%s", buf); + + /* Get the name */ + buf_ptr = buf; + while (W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) && *buf_ptr != ';' && buf_ptr-buf < sizeof(buf)) { + buf_ptr++; + } + value_name = talloc_strdup(mem_ctx, buf); + + /* Get the type */ + if (read(fd, &value_type, 4) < 4) { + DEBUG(0, ("Error while reading PReg\n")); + close(fd); + return WERR_GENERAL_FAILURE; + } + /* Read past delimiter */ + buf_ptr = buf; + if (!(W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) && *buf_ptr == ';') && buf_ptr-buf < sizeof(buf)) { + DEBUG(0, ("Error in PReg file.\n")); + close(fd); + return WERR_GENERAL_FAILURE; + } + /* Get data length */ + if (read(fd, &length, 4) < 4) { + DEBUG(0, ("Error while reading PReg\n")); + close(fd); + return WERR_GENERAL_FAILURE; + } + /* Read past delimiter */ + buf_ptr = buf; + if (!(W_ERROR_IS_OK(preg_read_utf16(fd, buf_ptr)) && *buf_ptr == ';') && buf_ptr-buf < sizeof(buf)) { + DEBUG(0, ("Error in PReg file.\n")); + close(fd); + return WERR_GENERAL_FAILURE; + } + /* Get the data */ + buf_ptr = buf; + if (length < sizeof(buf) && read(fd, buf_ptr, length) != length) { + DEBUG(0, ("Error while reading PReg\n")); + close(fd); + return WERR_GENERAL_FAILURE; + } + data.length = length; + data.data = talloc_memdup(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 < sizeof(buf)) { + 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); + talloc_free(data.data); + } + close(fd); + return WERR_OK; +} diff --git a/source4/lib/registry/reg_backend_dir.c b/source4/lib/registry/reg_backend_dir.c deleted file mode 100644 index c2dd3dad00..0000000000 --- a/source4/lib/registry/reg_backend_dir.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - Unix SMB/CIFS implementation. - Registry interface - Copyright (C) Jelmer Vernooij 2004. - - 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 "system/dir.h" -#include "system/filesys.h" - -static WERROR reg_dir_add_key(TALLOC_CTX *mem_ctx, const struct registry_key *parent, const char *name, uint32_t access_mask, struct security_descriptor *desc, struct registry_key **result) -{ - char *path; - int ret; - asprintf(&path, "%s%s\\%s", parent->hive->location, parent->path, name); - path = reg_path_win2unix(path); - ret = mkdir(path, 0700); - SAFE_FREE(path); - if(ret == 0)return WERR_OK; /* FIXME */ - return WERR_INVALID_PARAM; -} - -static WERROR reg_dir_del_key(const struct registry_key *k, const char *name) -{ - char *child = talloc_asprintf(NULL, "%s/%s", (char *)k->backend_data, name); - WERROR ret; - - if (rmdir(child) == 0) ret = WERR_OK; else ret = WERR_GENERAL_FAILURE; - - talloc_free(child); - - return ret; -} - -static WERROR reg_dir_open_key(TALLOC_CTX *mem_ctx, const struct registry_key *p, const char *name, struct registry_key **subkey) -{ - DIR *d; - char *fullpath, *unixpath; - struct registry_key *ret; - - if(!name) { - DEBUG(0, ("NULL pointer passed as directory name!")); - return WERR_INVALID_PARAM; - } - - - fullpath = talloc_asprintf(mem_ctx, "%s/%s", (char *)p->backend_data, name); - unixpath = reg_path_win2unix(fullpath); - - d = opendir(unixpath); - if(!d) { - DEBUG(3,("Unable to open '%s': %s\n", unixpath, strerror(errno))); - return WERR_BADFILE; - } - closedir(d); - ret = talloc(mem_ctx, struct registry_key); - ret->hive = p->hive; - ret->path = fullpath; - ret->backend_data = unixpath; - *subkey = ret; - return WERR_OK; -} - -static WERROR reg_dir_key_by_index(TALLOC_CTX *mem_ctx, const struct registry_key *k, int idx, struct registry_key **key) -{ - struct dirent *e; - char *fullpath = k->backend_data; - int i = 0; - DIR *d; - - d = opendir(fullpath); - - if(!d) return WERR_INVALID_PARAM; - - while((e = readdir(d))) { - if(!ISDOT(e->d_name) && !ISDOTDOT(e->d_name)) { - struct stat stbuf; - char *thispath; - - /* Check if file is a directory */ - asprintf(&thispath, "%s/%s", fullpath, e->d_name); - stat(thispath, &stbuf); - - if(S_ISDIR(stbuf.st_mode)) { - if(i == idx) { - (*key) = talloc(mem_ctx, struct registry_key); - (*key)->name = talloc_strdup(*key, e->d_name); - (*key)->path = NULL; - (*key)->backend_data = talloc_strdup(*key, thispath); - SAFE_FREE(thispath); - closedir(d); - return WERR_OK; - } - i++; - } - - SAFE_FREE(thispath); - } - } - - closedir(d); - - return WERR_NO_MORE_ITEMS; -} - -static WERROR reg_dir_open(struct registry_hive *h, struct registry_key **key) -{ - if(!h->location) return WERR_INVALID_PARAM; - - *key = talloc(h, struct registry_key); - (*key)->backend_data = talloc_strdup(*key, h->location); - return WERR_OK; -} - -static struct hive_operations reg_backend_dir = { - .name = "dir", - .open_hive = reg_dir_open, - .open_key = reg_dir_open_key, - .add_key = reg_dir_add_key, - .del_key = reg_dir_del_key, - .get_subkey_by_index = reg_dir_key_by_index -}; - -NTSTATUS registry_dir_init(void) -{ - return registry_register(®_backend_dir); -} diff --git a/source4/lib/registry/reg_backend_ldb.c b/source4/lib/registry/reg_backend_ldb.c deleted file mode 100644 index ca9327c174..0000000000 --- a/source4/lib/registry/reg_backend_ldb.c +++ /dev/null @@ -1,405 +0,0 @@ -/* - Unix SMB/CIFS implementation. - Registry interface - Copyright (C) Jelmer Vernooij 2004. - - 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 "lib/ldb/include/ldb.h" -#include "lib/ldb/include/ldb_errors.h" -#include "db_wrap.h" -#include "librpc/gen_ndr/winreg.h" - -struct ldb_key_data -{ - struct ldb_dn *dn; - struct ldb_message **subkeys, **values; - int subkey_count, value_count; -}; - -static int ldb_free_hive (struct registry_hive *hive) -{ - talloc_free(hive->backend_data); - hive->backend_data = NULL; - return 0; -} - -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; - *name = talloc_strdup(mem_ctx, ldb_msg_find_attr_as_string(msg, "value", NULL)); - *type = ldb_msg_find_attr_as_uint(msg, "type", 0); - val = ldb_msg_find_ldb_val(msg, "data"); - - switch (*type) - { - case REG_SZ: - case REG_EXPAND_SZ: - data->length = convert_string_talloc(mem_ctx, CH_UTF8, CH_UTF16, val->data, val->length, (void **)&data->data); - break; - - case REG_DWORD: { - uint32_t tmp = strtoul((char *)val->data, NULL, 0); - *data = data_blob_talloc(mem_ctx, &tmp, 4); - } - break; - - default: - *data = data_blob_talloc(mem_ctx, val->data, val->length); - 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_val val; - struct ldb_message *msg = talloc_zero(mem_ctx, struct ldb_message); - char *type_s; - - ldb_msg_add_string(msg, "value", talloc_strdup(mem_ctx, name)); - - switch (type) { - case REG_SZ: - case REG_EXPAND_SZ: - val.length = convert_string_talloc(mem_ctx, CH_UTF16, CH_UTF8, (void *)data.data, data.length, (void **)&val.data); - ldb_msg_add_value(msg, "data", &val, NULL); - break; - - case REG_DWORD: - ldb_msg_add_string(msg, "data", talloc_asprintf(mem_ctx, "0x%x", IVAL(data.data, 0))); - break; - default: - ldb_msg_add_value(msg, "data", &data, NULL); - } - - - type_s = talloc_asprintf(mem_ctx, "%u", type); - ldb_msg_add_string(msg, "type", type_s); - - return msg; -} - - -static int reg_close_ldb_key(struct registry_key *key) -{ - struct ldb_key_data *kd = talloc_get_type(key->backend_data, struct ldb_key_data); -/* struct ldb_context *c = key->hive->backend_data; */ - - if (kd->subkeys) { - talloc_free(kd->subkeys); - kd->subkeys = NULL; - } - - if (kd->values) { - talloc_free(kd->values); - kd->values = NULL; - } - return 0; -} - -static struct ldb_dn *reg_path_to_ldb(TALLOC_CTX *mem_ctx, const struct registry_key *from, const char *path, const char *add) -{ - TALLOC_CTX *local_ctx; - struct ldb_dn *ret; - char *mypath = talloc_strdup(mem_ctx, path); - char *begin; - struct ldb_key_data *kd = talloc_get_type(from->backend_data, struct ldb_key_data); - struct ldb_context *ldb = talloc_get_type(from->hive->backend_data, struct ldb_context); - - local_ctx = talloc_new(mem_ctx); - - if (add) { - ret = ldb_dn_new(mem_ctx, ldb, add); - } else { - ret = ldb_dn_new(mem_ctx, ldb, NULL); - } - if ( ! ldb_dn_validate(ret)) { - talloc_free(ret); - talloc_free(local_ctx); - return NULL; - } - - while(mypath) { - char *keyname; - - begin = strrchr(mypath, '\\'); - - if (begin) keyname = begin + 1; - else keyname = mypath; - - if(strlen(keyname)) { - ldb_dn_add_base_fmt(ret, "key=%s", keyname); - } - - if(begin) { - *begin = '\0'; - } else { - break; - } - } - - ldb_dn_add_base(ret, kd->dn); - - talloc_free(local_ctx); - - return ret; -} - - -static WERROR ldb_get_subkey_by_id(TALLOC_CTX *mem_ctx, const struct registry_key *k, int idx, struct registry_key **subkey) -{ - struct ldb_context *c = talloc_get_type(k->hive->backend_data, struct ldb_context); - struct ldb_message_element *el; - struct ldb_key_data *kd = talloc_get_type(k->backend_data, struct ldb_key_data); - struct ldb_key_data *newkd; - - /* Do a search if necessary */ - if (kd->subkeys == NULL) { - struct ldb_result *res; - int ret; - - ret = ldb_search(c, kd->dn, LDB_SCOPE_ONELEVEL, "(key=*)", NULL, &res); - - 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); - } - - if (idx >= kd->subkey_count) return WERR_NO_MORE_ITEMS; - - el = ldb_msg_find_element(kd->subkeys[idx], "key"); - - *subkey = talloc(mem_ctx, struct registry_key); - talloc_set_destructor(*subkey, reg_close_ldb_key); - (*subkey)->name = talloc_strdup(mem_ctx, (char *)el->values[0].data); - (*subkey)->backend_data = newkd = talloc_zero(*subkey, struct ldb_key_data); - (*subkey)->last_mod = 0; /* TODO: we need to add this to the - ldb backend properly */ - newkd->dn = ldb_dn_copy(mem_ctx, kd->subkeys[idx]->dn); - - return WERR_OK; -} - -static WERROR ldb_get_value_by_id(TALLOC_CTX *mem_ctx, const struct registry_key *k, int idx, struct registry_value **value) -{ - struct ldb_context *c = talloc_get_type(k->hive->backend_data, struct ldb_context); - struct ldb_key_data *kd = talloc_get_type(k->backend_data, struct ldb_key_data); - - /* Do the search if necessary */ - if (kd->values == NULL) { - struct ldb_result *res; - int ret; - - ret = ldb_search(c, kd->dn, LDB_SCOPE_ONELEVEL, "(value=*)", NULL, &res); - - 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); - } - - if(idx >= kd->value_count) return WERR_NO_MORE_ITEMS; - - *value = talloc(mem_ctx, struct registry_value); - - reg_ldb_unpack_value(mem_ctx, kd->values[idx], &(*value)->name, &(*value)->data_type, &(*value)->data); - - return WERR_OK; -} - -static WERROR ldb_open_key(TALLOC_CTX *mem_ctx, const struct registry_key *h, const char *name, struct registry_key **key) -{ - struct ldb_context *c = talloc_get_type(h->hive->backend_data, struct ldb_context); - struct ldb_result *res; - struct ldb_dn *ldap_path; - int ret; - struct ldb_key_data *newkd; - - ldap_path = reg_path_to_ldb(mem_ctx, h, name, NULL); - - ret = ldb_search(c, ldap_path, LDB_SCOPE_BASE, "(key=*)", NULL, &res); - - if (ret != LDB_SUCCESS) { - DEBUG(0, ("Error opening key '%s': %s\n", ldb_dn_get_linearized(ldap_path), ldb_errstring(c))); - return WERR_FOOBAR; - } else if (res->count == 0) { - talloc_free(res); - return WERR_BADFILE; - } - - *key = talloc(mem_ctx, struct registry_key); - talloc_set_destructor(*key, reg_close_ldb_key); - (*key)->name = talloc_strdup(mem_ctx, strrchr(name, '\\')?strchr(name, '\\'):name); - (*key)->backend_data = newkd = talloc_zero(*key, struct ldb_key_data); - newkd->dn = ldb_dn_copy(mem_ctx, res->msgs[0]->dn); - - talloc_free(res); - - return WERR_OK; -} - -static WERROR ldb_open_hive(struct registry_hive *hive, struct registry_key **k) -{ - struct ldb_key_data *kd; - struct ldb_context *wrap; - - if (!hive->location) return WERR_INVALID_PARAM; - - wrap = ldb_wrap_connect(hive, hive->location, hive->session_info, hive->credentials, 0, NULL); - - if(!wrap) { - DEBUG(1, ("ldb_open_hive: unable to connect\n")); - return WERR_FOOBAR; - } - - ldb_set_debug_stderr(wrap); - hive->backend_data = wrap; - - *k = talloc_zero(hive, struct registry_key); - talloc_set_destructor (*k, reg_close_ldb_key); - talloc_set_destructor (hive, ldb_free_hive); - (*k)->name = talloc_strdup(*k, ""); - (*k)->backend_data = kd = talloc_zero(*k, struct ldb_key_data); - kd->dn = ldb_dn_new(*k, wrap, "hive=NONE"); - - - return WERR_OK; -} - -static WERROR ldb_add_key (TALLOC_CTX *mem_ctx, const struct registry_key *parent, const char *name, uint32_t access_mask, struct security_descriptor *sd, struct registry_key **newkey) -{ - struct ldb_context *ctx = talloc_get_type(parent->hive->backend_data, struct ldb_context); - struct ldb_message *msg; - struct ldb_key_data *newkd; - int ret; - - msg = ldb_msg_new(mem_ctx); - - msg->dn = reg_path_to_ldb(msg, parent, name, NULL); - - ldb_msg_add_string(msg, "key", talloc_strdup(mem_ctx, name)); - - ret = ldb_add(ctx, msg); - if (ret < 0) { - DEBUG(1, ("ldb_msg_add: %s\n", ldb_errstring(ctx))); - return WERR_FOOBAR; - } - - *newkey = talloc_zero(mem_ctx, struct registry_key); - (*newkey)->name = talloc_strdup(mem_ctx, name); - - (*newkey)->backend_data = newkd = talloc_zero(*newkey, struct ldb_key_data); - newkd->dn = talloc_steal(newkd, msg->dn); - - return WERR_OK; -} - -static WERROR ldb_del_key (const struct registry_key *key, const char *child) -{ - struct ldb_context *ctx = talloc_get_type(key->hive->backend_data, struct ldb_context); - int ret; - struct ldb_key_data *kd = talloc_get_type(key->backend_data, struct ldb_key_data); - struct ldb_dn *childdn; - - childdn = ldb_dn_copy(ctx, kd->dn); - ldb_dn_add_child_fmt(childdn, "key=%s", child); - - ret = ldb_delete(ctx, childdn); - - talloc_free(childdn); - - if (ret < 0) { - DEBUG(1, ("ldb_del_key: %s\n", ldb_errstring(ctx))); - return WERR_FOOBAR; - } - - return WERR_OK; -} - -static WERROR ldb_del_value (const struct registry_key *key, const char *child) -{ - int ret; - struct ldb_context *ctx = talloc_get_type(key->hive->backend_data, struct ldb_context); - struct ldb_key_data *kd = talloc_get_type(key->backend_data, struct ldb_key_data); - struct ldb_dn *childdn; - - childdn = ldb_dn_copy(ctx, kd->dn); - ldb_dn_add_child_fmt(childdn, "value=%s", child); - - ret = ldb_delete(ctx, childdn); - - talloc_free(childdn); - - if (ret < 0) { - DEBUG(1, ("ldb_del_value: %s\n", ldb_errstring(ctx))); - return WERR_FOOBAR; - } - - return WERR_OK; -} - -static WERROR ldb_set_value (const struct registry_key *parent, const char *name, uint32_t type, DATA_BLOB data) -{ - struct ldb_context *ctx = talloc_get_type(parent->hive->backend_data, struct ldb_context); - struct ldb_message *msg; - struct ldb_key_data *kd = talloc_get_type(parent->backend_data, struct ldb_key_data); - int ret; - TALLOC_CTX *mem_ctx = talloc_init("ldb_set_value"); - - msg = reg_ldb_pack_value(ctx, mem_ctx, name, type, data); - - msg->dn = ldb_dn_copy(msg, kd->dn); - ldb_dn_add_child_fmt(msg->dn, "value=%s", name); - - ret = ldb_add(ctx, msg); - if (ret < 0) { - ret = ldb_modify(ctx, msg); - if (ret < 0) { - DEBUG(1, ("ldb_msg_add: %s\n", ldb_errstring(ctx))); - talloc_free(mem_ctx); - return WERR_FOOBAR; - } - } - - talloc_free(mem_ctx); - return WERR_OK; -} - -static struct hive_operations reg_backend_ldb = { - .name = "ldb", - .add_key = ldb_add_key, - .del_key = ldb_del_key, - .open_hive = ldb_open_hive, - .open_key = ldb_open_key, - .get_value_by_index = ldb_get_value_by_id, - .get_subkey_by_index = ldb_get_subkey_by_id, - .set_value = ldb_set_value, - .del_value = ldb_del_value, -}; - -NTSTATUS registry_ldb_init(void) -{ - return registry_register(®_backend_ldb); -} diff --git a/source4/lib/registry/reg_backend_nt4.c b/source4/lib/registry/reg_backend_nt4.c deleted file mode 100644 index 74261c57a9..0000000000 --- a/source4/lib/registry/reg_backend_nt4.c +++ /dev/null @@ -1,1124 +0,0 @@ -/* - Samba CIFS implementation - Registry backend for REGF files - Copyright (C) 2005 Jelmer Vernooij, jelmer@samba.org - Copyright (C) 2006 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 "lib/registry/registry.h" -#include "system/filesys.h" -#include "system/time.h" -#include "lib/registry/tdr_regf.h" -#include "librpc/gen_ndr/ndr_security.h" - -/* TODO: - * - Return error codes that make more sense - * - Locking - */ - -/* - * Read HBIN blocks into memory - */ - -struct regf_data { - int fd; - struct hbin_block **hbins; - struct regf_hdr *header; -}; - -static struct hbin_block *hbin_by_offset (const struct regf_data *data, uint32_t offset, uint32_t *rel_offset) -{ - 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) - *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; - int i; - - for (i = 0; i < 0x01FB; i+= 4) { - x = IVAL(buffer, i); - checksum ^= x; - } - - return checksum; -} - -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 containing 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; - - ZERO_STRUCT(pull); - - pull.data = hbin_get(regf, offset); - if (!pull.data.data) { - DEBUG(1, ("Unable to get data at 0x%04x\n", offset)); - return False; - } - - if (NT_STATUS_IS_ERR(pull_fn(&pull, ctx, p))) { - DEBUG(1, ("Error parsing record at 0x%04x using tdr\n", offset)); - return False; - } - - 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 = -1; /* Relative offset ! */ - struct hbin_block *hbin = NULL; - int i; - - *offset = 0; - - if (size == 0) - return data_blob(NULL, 0); - - size += 4; /* Need to include uint32 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; - uint32_t my_size; - for (j = 0; j < hbin->offset_to_next-0x20; j+= my_size) { - uint32_t header = IVAL(hbin->data, j + 4); - my_size = IVAL(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 & 0x80000000) { /* Used... */ - my_size = (my_size ^ 0xffffffff) + 1; - } 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; - 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 (header == 0xffffffff && - hbin->offset_to_next-rel_offset >= size) { - rel_offset = j; - - DEBUG(4, ("Found free block of size %d at end of HBIN\n", size)); - /* Mark new free block size */ - SIVAL(hbin->data, rel_offset+size,hbin->offset_to_next - rel_offset - size - 0x20); - SIVAL(hbin->data, rel_offset+size+0x4, 0xffffffff); - break; - } - - if (header == 0xffffffff) { - 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)); - data->hbins = talloc_realloc(data, data->hbins, struct hbin_block *, i+2); - hbin = talloc(data->hbins, struct hbin_block); - data->hbins[i] = hbin; - data->hbins[i+1] = NULL; - - 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[0] = 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); - - rel_offset = 0x0; - SIVAL(hbin->data, size, hbin->block_size - size - 0x20); - SIVAL(hbin->data, size + 0x4, 0xffffffff); - } - - /* Set size and mark as used */ - SIVAL(hbin->data, rel_offset, size | 0x80000000); - - ret.data = hbin->data + rel_offset + 0x4; /* Skip past length */ - ret.length = size - 0x4; - if (offset) { - uint32_t new_rel_offset; - *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); - - return ret; -} - -static uint32_t hbin_store_tdr (struct regf_data *data, tdr_push_fn_t push_fn, void *p) -{ - struct tdr_push *push = talloc_zero(data, struct tdr_push); - 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) -{ - uint32_t size; - uint32_t rel_offset; - struct hbin_block *hbin; - - SMB_ASSERT (offset > 0); - - hbin = hbin_by_offset(data, offset, &rel_offset); - - if (hbin == NULL) - return; - - /* Get original size */ - size = IVAL(hbin->data, rel_offset); - - if (!(size & 0x80000000)) { - DEBUG(1, ("Trying to free already freed block at 0x%04x\n", offset)); - return; - } - - /* Mark block as free */ - SIVAL(hbin->data, rel_offset, size &~ 0x80000000); -} - -/* Store a data blob data was already stored, but hsa 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); - uint32_t my_size; - uint32_t orig_size; - uint32_t needed_size; - uint32_t possible_size; - int i; - - SMB_ASSERT(orig_offset > 0); - - if (!hbin) - return hbin_store(data, blob); - - /* Get original size */ - orig_size = IVAL(hbin->data, rel_offset); - - needed_size = blob.length + 4; /* Add uint32 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); - 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) { - uint32_t header; - if (IVAL(hbin->data, i) & 0x80000000) /* Used */ - break; - - my_size = IVAL(hbin->data, i); - header = IVAL(hbin->data, i + 4); - if (header == 0xffffffff) { - possible_size = hbin->offset_to_next - 0x20 - rel_offset; - } else 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; - } - - if (header == 0xffffffff) - break; - } - - 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 = talloc_zero(regf, struct tdr_push); - 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 WERROR regf_num_subkeys (const struct registry_key *key, uint32_t *count) -{ - struct nk_block *nk = key->backend_data; - - *count = nk->num_subkeys; - - return WERR_OK; -} - -static WERROR regf_num_values (const struct registry_key *key, uint32_t *count) -{ - struct nk_block *nk = key->backend_data; - - *count = nk->num_values; - - return WERR_OK; -} - -static struct registry_key *regf_get_key (TALLOC_CTX *ctx, struct regf_data *regf, uint32_t offset) -{ - struct registry_key *ret; - struct nk_block *nk; - - ret = talloc_zero(ctx, struct registry_key); - nk = talloc(ret, struct nk_block); - 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 %d\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; - } - - ret->name = talloc_steal(ret, nk->key_name); - ret->last_mod = nk->last_change; - - if (nk->clsname_offset != -1) { - DATA_BLOB data = hbin_get(regf, nk->clsname_offset); - ret->class_name = talloc_strndup(ret, (char*)data.data, nk->clsname_length); - } - ret->backend_data = nk; - - return ret; -} - -static WERROR regf_get_value (TALLOC_CTX *ctx, const struct registry_key *key, int idx, struct registry_value **ret) -{ - struct nk_block *nk = key->backend_data; - struct vk_block *vk; - struct regf_data *regf = key->hive->backend_data; - uint32_t vk_offset; - DATA_BLOB data; - - if (idx >= nk->num_values) - return WERR_NO_MORE_ITEMS; - - data = hbin_get(regf, nk->values_offset); - if (!data.data) { - DEBUG(0, ("Unable to find value list\n")); - return WERR_GENERAL_FAILURE; - } - - if (data.length < nk->num_values * 4) { - DEBUG(1, ("Value counts mismatch\n")); - } - - vk_offset = IVAL(data.data, idx * 4); - - *ret = talloc_zero(ctx, struct registry_value); - if (!(*ret)) - return WERR_NOMEM; - - vk = talloc(*ret, struct vk_block); - if (!vk) - return WERR_NOMEM; - - 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 %d\n", vk_offset)); - return WERR_GENERAL_FAILURE; - } - - (*ret)->name = talloc_steal(*ret, vk->data_name); - (*ret)->data_type = vk->data_type; - if (vk->data_length & 0x80000000) { - vk->data_length &=~0x80000000; - (*ret)->data.data = (uint8_t *)&vk->data_offset; - (*ret)->data.length = vk->data_length; - } else { - (*ret)->data = hbin_get(regf, vk->data_offset); - } - - if ((*ret)->data.length < vk->data_length) { - DEBUG(1, ("Read data less than indicated data length!\n")); - } - - return WERR_OK; -} - -static WERROR regf_get_subkey_by_index (TALLOC_CTX *ctx, const struct registry_key *key, int idx, struct registry_key **ret) -{ - DATA_BLOB data; - struct nk_block *nk = key->backend_data; - uint32_t key_off=0; - - if (idx >= nk->num_subkeys) - return WERR_NO_MORE_ITEMS; - - data = hbin_get(key->hive->backend_data, nk->subkeys_offset); - if (!data.data) { - DEBUG(0, ("Unable to find subkey list\n")); - return WERR_GENERAL_FAILURE; - } - - if (!strncmp((char *)data.data, "li", 2)) { - struct li_block li; - struct tdr_pull pull; - - DEBUG(10, ("Subkeys in LI list\n")); - ZERO_STRUCT(pull); - pull.data = data; - - if (NT_STATUS_IS_ERR(tdr_pull_li_block(&pull, nk, &li))) { - DEBUG(0, ("Error parsing LI list\n")); - return WERR_GENERAL_FAILURE; - } - SMB_ASSERT(!strncmp(li.header, "li",2)); - - if (li.key_count != nk->num_subkeys) { - DEBUG(0, ("Subkey counts don't match\n")); - return WERR_GENERAL_FAILURE; - } - key_off = li.nk_offset[idx]; - - } else if (!strncmp((char *)data.data, "lf", 2)) { - struct lf_block lf; - struct tdr_pull pull; - - DEBUG(10, ("Subkeys in LF list\n")); - ZERO_STRUCT(pull); - pull.data = data; - - if (NT_STATUS_IS_ERR(tdr_pull_lf_block(&pull, nk, &lf))) { - DEBUG(0, ("Error parsing LF list\n")); - return WERR_GENERAL_FAILURE; - } - SMB_ASSERT(!strncmp(lf.header, "lf",2)); - - if (lf.key_count != nk->num_subkeys) { - DEBUG(0, ("Subkey counts don't match\n")); - return WERR_GENERAL_FAILURE; - } - - key_off = lf.hr[idx].nk_offset; - } else if (!strncmp((char *)data.data, "lh", 2)) { - struct lh_block lh; - struct tdr_pull pull; - - DEBUG(10, ("Subkeys in LH list")); - ZERO_STRUCT(pull); - pull.data = data; - - if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, nk, &lh))) { - DEBUG(0, ("Error parsing LH list\n")); - return WERR_GENERAL_FAILURE; - } - SMB_ASSERT(!strncmp(lh.header, "lh",2)); - - if (lh.key_count != nk->num_subkeys) { - DEBUG(0, ("Subkey counts don't match\n")); - return WERR_GENERAL_FAILURE; - } - key_off = lh.hr[idx].nk_offset; - } else if (!strncmp((char *)data.data, "ri", 2)) { - struct ri_block ri; - struct tdr_pull pull; - uint16_t i; - uint16_t sublist_count = 0; - - ZERO_STRUCT(pull); - pull.data = data; - - if (NT_STATUS_IS_ERR(tdr_pull_ri_block(&pull, nk, &ri))) { - DEBUG(0, ("Error parsing RI list\n")); - return WERR_GENERAL_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(key->hive->backend_data, ri.offset[i]); - if (!list_data.data) { - DEBUG(0, ("Error getting RI list.")); - return WERR_GENERAL_FAILURE; - } - - ZERO_STRUCT(pull); - 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")); - return WERR_GENERAL_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; - - if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, nk, &lh))) { - DEBUG(0, ("Error parsing LH list from RI\n")); - return WERR_GENERAL_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")); - SMB_ASSERT(0); - } - - } - - 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_GENERAL_FAILURE; - } - - *ret = regf_get_key (ctx, key->hive->backend_data, key_off); - - return WERR_OK; -} - -static WERROR regf_match_subkey_by_name (TALLOC_CTX *ctx, const struct registry_key *key, uint32_t offset, const char *name, uint32_t *ret) -{ - DATA_BLOB subkey_data; - struct nk_block subkey; - struct tdr_pull pull; - - subkey_data = hbin_get(key->hive->backend_data, offset); - if (!subkey_data.data) { - DEBUG(0, ("Unable to retrieve subkey HBIN\n")); - return WERR_GENERAL_FAILURE; - } - - ZERO_STRUCT(pull); - pull.data = subkey_data; - - if (NT_STATUS_IS_ERR(tdr_pull_nk_block(&pull, ctx, &subkey))) { - DEBUG(0, ("Error parsing NK structure.\n")); - return WERR_GENERAL_FAILURE; - } - if (strncmp(subkey.header, "nk", 2)) { - DEBUG(0, ("Not an NK structure.\n")); - return WERR_GENERAL_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 registry_key *key, const char *name, struct registry_key **ret) -{ - DATA_BLOB data; - struct nk_block *nk = key->backend_data; - uint32_t key_off = 0; - - data = hbin_get(key->hive->backend_data, nk->subkeys_offset); - if (!data.data) { - DEBUG(0, ("Unable to find subkey list\n")); - return WERR_GENERAL_FAILURE; - } - - if (!strncmp((char *)data.data, "li",2)) { - struct li_block li; - struct tdr_pull pull; - uint16_t i; - - DEBUG(10, ("Subkeys in LI list\n")); - ZERO_STRUCT(pull); - pull.data = data; - - if (NT_STATUS_IS_ERR(tdr_pull_li_block(&pull, nk, &li))) { - DEBUG(0, ("Error parsing LI list\n")); - return WERR_GENERAL_FAILURE; - } - SMB_ASSERT(!strncmp(li.header, "li",2)); - - if (li.key_count != nk->num_subkeys) { - DEBUG(0, ("Subkey counts don't match\n")); - return WERR_GENERAL_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) { - break; - } - } - if (!key_off) { - return WERR_DEST_NOT_FOUND; - } - } else if (!strncmp((char *)data.data, "lf",2)) { - struct lf_block lf; - struct tdr_pull pull; - uint16_t i; - - DEBUG(10, ("Subkeys in LF list\n")); - ZERO_STRUCT(pull); - pull.data = data; - - if (NT_STATUS_IS_ERR(tdr_pull_lf_block(&pull, nk, &lf))) { - DEBUG(0, ("Error parsing LF list\n")); - return WERR_GENERAL_FAILURE; - } - SMB_ASSERT(!strncmp(lf.header, "lf",2)); - - if (lf.key_count != nk->num_subkeys) { - DEBUG(0, ("Subkey counts don't match\n")); - return WERR_GENERAL_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) { - break; - } - } - if (!key_off) { - return WERR_DEST_NOT_FOUND; - } - } else if (!strncmp((char *)data.data, "lh",2)) { - struct lh_block lh; - struct tdr_pull pull; - uint16_t i; - uint32_t hash = 0; - char *hash_name; - - DEBUG(10, ("Subkeys in LH list\n")); - ZERO_STRUCT(pull); - pull.data = data; - - if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, nk, &lh))) { - DEBUG(0, ("Error parsing LH list\n")); - return WERR_GENERAL_FAILURE; - } - SMB_ASSERT(!strncmp(lh.header, "lh",2)); - - if (lh.key_count != nk->num_subkeys) { - DEBUG(0, ("Subkey counts don't match\n")); - return WERR_GENERAL_FAILURE; - } - - /* Compute hash for the name */ - hash_name = strupper_talloc(nk, name); - for (i = 0; *(hash_name + i) != 0; i++) { - hash *= 37; - hash += *(hash_name + i); - } - 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) { - break; - } - } - if (!key_off) { - return WERR_DEST_NOT_FOUND; - } - } else if (!strncmp((char *)data.data, "ri", 2)) { - struct ri_block ri; - struct tdr_pull pull; - uint16_t i, j; - - DEBUG(10, ("Subkeys in RI list\n")); - ZERO_STRUCT(pull); - pull.data = data; - - if (NT_STATUS_IS_ERR(tdr_pull_ri_block(&pull, nk, &ri))) { - DEBUG(0, ("Error parsing RI list\n")); - return WERR_GENERAL_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(key->hive->backend_data, ri.offset[i]); - if (!list_data.data) { - DEBUG(0, ("Error getting RI list.")); - return WERR_GENERAL_FAILURE; - } - - ZERO_STRUCT(pull); - 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")); - return WERR_GENERAL_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 = 0; - char *hash_name; - - if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, nk, &lh))) { - DEBUG(0, ("Error parsing LH list from RI\n")); - return WERR_GENERAL_FAILURE; - } - SMB_ASSERT(!strncmp(lh.header, "lh",2)); - - /* Compute hash for the name */ - hash_name = strupper_talloc(nk, name); - for (j = 0; *(hash_name + j) != 0; j++) { - hash *= 37; - hash += *(hash_name + j); - } - 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; - } - - } - if (!key_off) { - return WERR_DEST_NOT_FOUND; - } - } else { - DEBUG(0, ("Unknown subkey list type.\n")); - return WERR_GENERAL_FAILURE; - } - - *ret = regf_get_key (ctx, key->hive->backend_data, key_off); - return WERR_OK; -} - -static WERROR regf_set_sec_desc (const struct registry_key *key, const struct security_descriptor *sec_desc) -{ - /* FIXME */ - return WERR_NOT_SUPPORTED; -} - -static WERROR regf_get_sec_desc(TALLOC_CTX *ctx, const struct registry_key *key, struct security_descriptor **sd) -{ - struct nk_block *nk = key->backend_data; - struct sk_block sk; - struct regf_data *regf = key->hive->backend_data; - DATA_BLOB data; - - if (!hbin_get_tdr(regf, nk->sk_offset, ctx, (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) { - DEBUG(0, ("Unable to find security descriptor\n")); - return WERR_GENERAL_FAILURE; - } - - if (strcmp(sk.header, "sk") != 0) { - DEBUG(0, ("Expected 'sk', got '%s'\n", sk.header)); - return WERR_GENERAL_FAILURE; - } - - *sd = talloc(ctx, struct security_descriptor); - if (!*sd) - return WERR_NOMEM; - - data.data = sk.sec_desc; - data.length = sk.rec_size; - if (NT_STATUS_IS_ERR(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_GENERAL_FAILURE; - } - - return WERR_OK; -} - -static uint32_t lf_add_entry (struct regf_data *regf, uint32_t list_offset, const char *name, uint32_t key_offset) -{ - uint32_t ret; - struct lf_block lf; - - ZERO_STRUCT(lf); - - /* Add to subkeys list */ - if (list_offset == -1) { /* Need to create subkeys list */ - lf.header = "lf"; - } else { - if (!hbin_get_tdr(regf, list_offset, regf, (tdr_pull_fn_t)tdr_pull_lf_block, &lf)) { - DEBUG(0, ("Can't get subkeys list\n")); - return -1; - } - } - - lf.hr = talloc_realloc(regf, lf.hr, struct hash_record, lf.key_count+1); - lf.hr[lf.key_count].nk_offset = key_offset; - lf.hr[lf.key_count].hash = talloc_strndup(regf, name, 4); - lf.key_count++; - - ret = hbin_store_tdr_resize(regf, (tdr_push_fn_t)tdr_push_lf_block, list_offset, &lf); - - talloc_free(lf.hr); - - return ret; -} - -static WERROR regf_del_value (const struct registry_key *parent, const char *name) -{ - /* FIXME */ - return WERR_NOT_SUPPORTED; -} - - -static WERROR regf_del_key (const struct registry_key *parent, const char *name) -{ - struct nk_block *nk = parent->backend_data; - - SMB_ASSERT(nk); - - if (nk->subkeys_offset == -1) - return WERR_BADFILE; - - /* FIXME */ - - return WERR_NOT_SUPPORTED; -} - -static WERROR regf_add_key (TALLOC_CTX *ctx, const struct registry_key *parent, const char *name, uint32_t access_mask, struct security_descriptor *sec_desc, struct registry_key **ret) -{ - struct nk_block *parent_nk = parent->backend_data, nk; - struct regf_data *regf = parent->hive->backend_data; - uint32_t offset; - - nk.header = "nk"; - nk.type = REG_SUB_KEY; - unix_to_nt_time(&nk.last_change, time(NULL)); - nk.uk1 = 0; - nk.parent_offset = 0; /* FIXME */ - nk.num_subkeys = 0; - nk.uk2 = 0; - nk.subkeys_offset = -1; - nk.unknown_offset = -1; - nk.num_values = 0; - nk.sk_offset = 0; - memset(nk.unk3, 0, 5); - nk.clsname_offset = -1; - nk.clsname_length = 0; - nk.key_name = name; - - offset = hbin_store_tdr(regf, (tdr_push_fn_t) tdr_push_nk_block, &nk); - - parent_nk->subkeys_offset = lf_add_entry(regf, parent_nk->subkeys_offset, name, nk.parent_offset); - - parent_nk->num_subkeys++; - - hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_nk_block, nk.parent_offset, parent_nk); - - *ret = regf_get_key(ctx, regf, offset); - - /* FIXME: Set sec desc ! */ - return WERR_OK; -} - -static WERROR regf_set_value (const struct registry_key *key, const char *name, uint32_t type, const DATA_BLOB data) -{ - /* FIXME */ - - return WERR_NOT_SUPPORTED; -} - -#if 0 /* Unused */ - -static WERROR regf_save_hbin(struct registry_hive *hive, struct hbin_block *hbin) -{ - struct regf_data *regf = hive->backend_data; - - /* go to right offset */ - if (lseek(regf->fd, SEEK_SET, regf->header->data_offset + hbin->offset_from_first) == -1) { - DEBUG(0, ("Error lseeking in regf file\n")); - return WERR_GENERAL_FAILURE; - } - - if (NT_STATUS_IS_ERR(tdr_push_to_fd(regf->fd, (tdr_push_fn_t)tdr_push_hbin_block, hbin))) { - DEBUG(0, ("Error writing HBIN block\n")); - return WERR_GENERAL_FAILURE; - } - - return WERR_OK; -} - -#endif - -static WERROR nt_open_hive (struct registry_hive *h, struct registry_key **key) -{ - struct regf_data *regf; - struct regf_hdr *regf_hdr; - struct tdr_pull pull; - int i; - - regf = (struct regf_data *)talloc_zero(h, struct regf_data); - h->backend_data = regf; - - DEBUG(5, ("Attempting to load registry file\n")); - - /* Get the header */ - regf->fd = open(h->location, O_RDWR); - - if (regf->fd == -1) { - DEBUG(0,("Could not load file: %s, %s\n", h->location, - strerror(errno))); - return WERR_GENERAL_FAILURE; - } - - ZERO_STRUCT(pull); - pull.data.data = (uint8_t*)fd_load(regf->fd, &pull.data.length, regf); - - if (pull.data.data == NULL) { - DEBUG(0, ("Error reading data\n")); - return WERR_GENERAL_FAILURE; - } - - regf_hdr = talloc(regf, struct regf_hdr); - if (NT_STATUS_IS_ERR(tdr_pull_regf_hdr(&pull, regf_hdr, regf_hdr))) { - return WERR_GENERAL_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, h->location)); - } - - DEBUG(1, ("Registry '%s' read. Version %d.%d.%d.%d\n", - regf_hdr->description, regf_hdr->version.major, - regf_hdr->version.minor, regf_hdr->version.release, - regf_hdr->version.build)); - - /* - * Validate the header ... - */ - if (regf_hdr_checksum(pull.data.data) != regf_hdr->chksum) { - DEBUG(0, ("Registry file checksum error: %s: %d,%d\n", - h->location, regf_hdr->chksum, regf_hdr_checksum(pull.data.data))); - return WERR_GENERAL_FAILURE; - } - - pull.offset = 0x1000; - - i = 0; - /* Read in all hbin blocks */ - regf->hbins = talloc_array(regf, struct hbin_block *, 1); - 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); - - if (NT_STATUS_IS_ERR(tdr_pull_hbin_block(&pull, hbin, hbin))) { - DEBUG(0, ("[%d] Error parsing HBIN block\n", i)); - return WERR_FOOBAR; - } - - if (strcmp(hbin->HBIN_ID, "hbin") != 0) { - DEBUG(0, ("[%d] Expected 'hbin', got '%s'\n", i, hbin->HBIN_ID)); - return WERR_FOOBAR; - } - - regf->hbins[i] = hbin; - i++; - regf->hbins = talloc_realloc(regf, regf->hbins, struct hbin_block *, i+2); - regf->hbins[i] = NULL; - } - - DEBUG(1, ("%d HBIN blocks read\n", i)); - - *key = regf_get_key(h, regf, 0x20); - - return WERR_OK; -} - -static struct hive_operations reg_backend_nt4 = { - .name = "nt4", - .open_hive = nt_open_hive, - .num_subkeys = regf_num_subkeys, - .num_values = regf_num_values, - .get_subkey_by_index = regf_get_subkey_by_index, - .get_subkey_by_name = regf_get_subkey_by_name, - .get_value_by_index = regf_get_value, - .key_get_sec_desc = regf_get_sec_desc, - .key_set_sec_desc = regf_set_sec_desc, - .add_key = regf_add_key, - .set_value = regf_set_value, - .del_key = regf_del_key, - .del_value = regf_del_value, -}; - -NTSTATUS registry_nt4_init(void) -{ - return registry_register(®_backend_nt4); -} diff --git a/source4/lib/registry/reg_backend_w95.c b/source4/lib/registry/reg_backend_w95.c deleted file mode 100644 index a0b6e0164a..0000000000 --- a/source4/lib/registry/reg_backend_w95.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - Samba Unix/Linux SMB client utility libeditreg.c - Copyright (C) 2004 Jelmer Vernooij, jelmer@samba.org - - Backend for Windows '95 registry files. Explanation of file format - comes from http://www.cs.mun.ca/~michael/regutils/. - - 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 "system/filesys.h" -#include "system/shmem.h" - -/** - * The registry starts with a header that contains pointers to - * the rgdb. - * - * After the main header follows the RGKN header (key index table). - * The RGKN keys are listed after each other. They are put into - * blocks, the first having a length of 0x2000 bytes, the others - * being 0x1000 bytes long. - * - * After the RGKN header follow one or more RGDB blocks. These blocks - * contain keys. A key is followed by its name and its values. - * - * Values are followed by their name and then their data. - * - * Basically the idea is that the RGKN contains the associations between - * the keys and the RGDB contains the actual data. - */ - -typedef uint32_t DWORD; -typedef unsigned short WORD; - -typedef struct creg_block { - DWORD CREG_ID; /* CREG */ - DWORD uk1; - DWORD rgdb_offset; - DWORD chksum; - WORD num_rgdb; - WORD flags; - DWORD uk2; - DWORD uk3; - DWORD uk4; -} CREG_HDR; - -typedef struct rgkn_block { - DWORD RGKN_ID; /* RGKN */ - DWORD size; - DWORD root_offset; - DWORD free_offset; - DWORD flags; - DWORD chksum; - DWORD uk1; - DWORD uk2; -} RGKN_HDR; - -typedef struct reg_id { - WORD id; - WORD rgdb; -} REG_ID; - -typedef struct rgkn_key { - DWORD type; /* 0x00000000 = normal key, 0x80000000 = free block */ - DWORD hash; /* Contains either hash or size of free blocks that follows */ - DWORD next_free; - DWORD parent_offset; - DWORD first_child_offset; - DWORD next_offset; - REG_ID id; -} RGKN_KEY; - - -typedef struct rgdb_block { - DWORD RGDB_ID; /* RGDB */ - DWORD size; - DWORD unused_size; - WORD flags; - WORD section; - DWORD free_offset; /* -1 if there is no free space */ - WORD max_id; - WORD first_free_id; - DWORD uk1; - DWORD chksum; -} RGDB_HDR; - -typedef struct rgdb_key { - DWORD size; - REG_ID id; - DWORD used_size; - WORD name_len; - WORD num_values; - DWORD uk1; -} RGDB_KEY; - -typedef struct rgdb_value { - DWORD type; - DWORD uk1; - WORD name_len; - WORD data_len; -} RGDB_VALUE; - -typedef struct creg_struct_s { - int fd; - BOOL modified; - char *base; - struct stat sbuf; - CREG_HDR *creg_hdr; - RGKN_HDR *rgkn_hdr; - RGDB_KEY ***rgdb_keys; -} CREG; - -#if 0 /* unused */ -#define RGKN_START_SIZE 0x2000 -#define RGKN_INC_SIZE 0x1000 -#endif - -#define LOCN_RGKN(creg, o) ((RGKN_KEY *)((creg)->base + sizeof(CREG_HDR) + o)) -#define LOCN_RGDB_BLOCK(creg, o) (((creg)->base + (creg)->creg_hdr->rgdb_offset + o)) -#define LOCN_RGDB_KEY(creg, rgdb, id) ((RGDB_KEY *)((creg)->rgdb_keys[(rgdb)][(id)])) - -static DWORD str_to_dword(const char *a) { - int i; - unsigned long ret = 0; - for(i = strlen(a)-1; i >= 0; i--) { - ret = ret * 0x100 + a[i]; - } - return ret; -} - -#if 0 /* unused */ - -static DWORD calc_hash(const char *str) { - DWORD ret = 0; - int i; - for(i = 0; str[i] && str[i] != '\\'; i++) { - ret+=toupper(str[i]); - } - return ret; -} - -static void parse_rgkn_block(CREG *creg, off_t start_off, off_t end_off) -{ - off_t i; - for(i = start_off; end_off - i > sizeof(RGKN_KEY); i+= sizeof(RGKN_KEY)) { - RGKN_KEY *key = (RGKN_KEY *)LOCN_RGKN(creg, i); - if(key->type == 0) { - DEBUG(4,("Regular, id: %d, %d, parent: %x, firstchild: %x, next: %x hash: %lX\n", key->id.id, key->id.rgdb, key->parent_offset, key->first_child_offset, key->next_offset, (long)key->hash)); - } else if(key->type == 0x80000000) { - DEBUG(3,("free\n")); - i += key->hash; - } else { - DEBUG(0,("Invalid key type in RGKN: %0X\n", key->type)); - } - } -} - -#endif - -static void parse_rgdb_block(CREG *creg, RGDB_HDR *rgdb_hdr) -{ - DWORD used_size = rgdb_hdr->size - rgdb_hdr->unused_size; - DWORD offset = 0; - - while(offset < used_size) { - RGDB_KEY *key = (RGDB_KEY *)(((char *)rgdb_hdr) + sizeof(RGDB_HDR) + offset); - - if(!(key->id.id == 0xFFFF && key->id.rgdb == 0xFFFF))creg->rgdb_keys[key->id.rgdb][key->id.id] = key; - offset += key->size; - } -} - -static WERROR w95_open_reg (struct registry_hive *h, struct registry_key **root) -{ - CREG *creg; - DWORD creg_id, rgkn_id; - DWORD i; - DWORD offset; - - creg = talloc(h, CREG); - memset(creg, 0, sizeof(CREG)); - h->backend_data = creg; - - if((creg->fd = open(h->location, O_RDONLY, 0000)) < 0) { - return WERR_FOOBAR; - } - - if (fstat(creg->fd, &creg->sbuf) < 0) { - return WERR_FOOBAR; - } - - creg->base = mmap(0, creg->sbuf.st_size, PROT_READ, MAP_SHARED, creg->fd, 0); - - if (creg->base == (void *)-1) { - DEBUG(0,("Could not mmap file: %s, %s\n", h->location, strerror(errno))); - return WERR_FOOBAR; - } - - creg->creg_hdr = (CREG_HDR *)creg->base; - - if ((creg_id = IVAL(&creg->creg_hdr->CREG_ID,0)) != str_to_dword("CREG")) { - DEBUG(0, ("Unrecognized Windows 95 registry header id: 0x%0X, %s\n", - creg_id, h->location)); - return WERR_FOOBAR; - } - - creg->rgkn_hdr = (RGKN_HDR *)LOCN_RGKN(creg, 0); - - if ((rgkn_id = IVAL(&creg->rgkn_hdr->RGKN_ID,0)) != str_to_dword("RGKN")) { - DEBUG(0, ("Unrecognized Windows 95 registry key index id: 0x%0X, %s\n", - rgkn_id, h->location)); - return WERR_FOOBAR; - } - -#if 0 - /* If'ed out because we only need to parse this stuff when allocating new - * entries (which we don't do at the moment */ - /* First parse the 0x2000 long block */ - parse_rgkn_block(creg, sizeof(RGKN_HDR), 0x2000); - - /* Then parse the other 0x1000 length blocks */ - for(offset = 0x2000; offset < creg->rgkn_hdr->size; offset+=0x1000) { - parse_rgkn_block(creg, offset, offset+0x1000); - } -#endif - - creg->rgdb_keys = talloc_array(h, RGDB_KEY **, creg->creg_hdr->num_rgdb); - - offset = 0; - DEBUG(3, ("Reading %d rgdb entries\n", creg->creg_hdr->num_rgdb)); - for(i = 0; i < creg->creg_hdr->num_rgdb; i++) { - RGDB_HDR *rgdb_hdr = (RGDB_HDR *)LOCN_RGDB_BLOCK(creg, offset); - - if(strncmp((char *)&(rgdb_hdr->RGDB_ID), "RGDB", 4)) { - DEBUG(0, ("unrecognized rgdb entry: %4d, %s\n", - rgdb_hdr->RGDB_ID, h->location)); - return WERR_FOOBAR; - } else { - DEBUG(3, ("Valid rgdb entry, first free id: %d, max id: %d\n", rgdb_hdr->first_free_id, rgdb_hdr->max_id)); - } - - - creg->rgdb_keys[i] = talloc_array(h, RGDB_KEY *, rgdb_hdr->max_id+1); - memset(creg->rgdb_keys[i], 0, sizeof(RGDB_KEY *) * (rgdb_hdr->max_id+1)); - - parse_rgdb_block(creg, rgdb_hdr); - - offset+=rgdb_hdr->size; - } - - /* First element in rgkn should be root key */ - *root = talloc(h, struct registry_key); - (*root)->name = NULL; - (*root)->backend_data = LOCN_RGKN(creg, sizeof(RGKN_HDR)); - - return WERR_OK; -} - -static WERROR w95_get_subkey_by_index (TALLOC_CTX *mem_ctx, const struct registry_key *parent, int n, struct registry_key **key) -{ - CREG *creg = parent->hive->backend_data; - RGKN_KEY *rgkn_key = parent->backend_data; - RGKN_KEY *child; - DWORD child_offset; - DWORD cur = 0; - - /* Get id of first child */ - child_offset = rgkn_key->first_child_offset; - - while(child_offset != 0xFFFFFFFF) { - child = LOCN_RGKN(creg, child_offset); - - /* n == cur ? return! */ - if(cur == n) { - RGDB_KEY *rgdb_key; - rgdb_key = LOCN_RGDB_KEY(creg, child->id.rgdb, child->id.id); - if(!rgdb_key) { - DEBUG(0, ("Can't find %d,%d in RGDB table!\n", child->id.rgdb, child->id.id)); - return WERR_FOOBAR; - } - *key = talloc(mem_ctx, struct registry_key); - (*key)->backend_data = child; - (*key)->name = talloc_strndup(mem_ctx, (char *)rgdb_key + sizeof(RGDB_KEY), rgdb_key->name_len); - return WERR_OK; - } - - cur++; - - child_offset = child->next_offset; - } - - return WERR_NO_MORE_ITEMS; -} - -static WERROR w95_num_values(const struct registry_key *k, uint32_t *count) -{ - RGKN_KEY *rgkn_key = k->backend_data; - RGDB_KEY *rgdb_key = LOCN_RGDB_KEY((CREG *)k->hive->backend_data, rgkn_key->id.rgdb, rgkn_key->id.id); - - if(!rgdb_key) return WERR_FOOBAR; - - *count = rgdb_key->num_values; - - return WERR_OK; -} - -static WERROR w95_get_value_by_id(TALLOC_CTX *mem_ctx, const struct registry_key *k, int idx, struct registry_value **value) -{ - RGKN_KEY *rgkn_key = k->backend_data; - DWORD i; - DWORD offset = 0; - RGDB_KEY *rgdb_key = LOCN_RGDB_KEY((CREG *)k->hive->backend_data, rgkn_key->id.rgdb, rgkn_key->id.id); - RGDB_VALUE *curval = NULL; - - if(!rgdb_key) return WERR_FOOBAR; - - if(idx >= rgdb_key->num_values) return WERR_NO_MORE_ITEMS; - - for(i = 0; i < idx; i++) { - curval = (RGDB_VALUE *)(((char *)rgdb_key) + sizeof(RGDB_KEY) + rgdb_key->name_len + offset); - offset+=sizeof(RGDB_VALUE) + curval->name_len + curval->data_len; - } - - *value = talloc(mem_ctx, struct registry_value); - (*value)->name = talloc_strndup(mem_ctx, (char *)curval+sizeof(RGDB_VALUE), curval->name_len); - - (*value)->data = data_blob_talloc(mem_ctx, curval+sizeof(RGDB_VALUE)+curval->name_len, curval->data_len); - (*value)->data_type = curval->type; - - return WERR_OK; -} - -static struct hive_operations reg_backend_w95 = { - .name = "w95", - .open_hive = w95_open_reg, - .get_value_by_index = w95_get_value_by_id, - .num_values = w95_num_values, - .get_subkey_by_index = w95_get_subkey_by_index, -}; - -NTSTATUS registry_w95_init(void) -{ - return registry_register(®_backend_w95); -} diff --git a/source4/lib/registry/regf.c b/source4/lib/registry/regf.c new file mode 100644 index 0000000000..7fa71033d9 --- /dev/null +++ b/source4/lib/registry/regf.c @@ -0,0 +1,1923 @@ +/* + Samba CIFS implementation + Registry backend for REGF files + Copyright (C) 2005-2007 Jelmer Vernooij, jelmer@samba.org + Copyright (C) 2006 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 "lib/registry/hive.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" + +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; +}; + +static WERROR regf_save_hbin(struct regf_data *data); + +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) +{ + 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; + 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 containing 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; + + ZERO_STRUCT(pull); + + pull.data = hbin_get(regf, offset); + if (!pull.data.data) { + DEBUG(1, ("Unable to get data at 0x%04x\n", offset)); + return false; + } + + if (NT_STATUS_IS_ERR(pull_fn(&pull, ctx, p))) { + DEBUG(1, ("Error parsing record at 0x%04x using tdr\n", offset)); + return false; + } + + 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 = -1; /* Relative offset ! */ + struct hbin_block *hbin = NULL; + int i; + + *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)); + 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; + + 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[0] = 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); + + rel_offset = 0x0; + SIVAL(hbin->data, size, hbin->block_size - size - 0x20); + } + + /* 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; + *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); + + return ret; +} + +static uint32_t hbin_store_tdr (struct regf_data *data, tdr_push_fn_t push_fn, void *p) +{ + struct tdr_push *push = talloc_zero(data, struct tdr_push); + 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) { + 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; + 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 = talloc_zero(regf, struct tdr_push); + 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) +{ + 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); + } else + *classname = NULL; + } + + /* TODO: Last mod time */ + + 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 %d\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, const struct hive_key *key, + int 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\n")); + return WERR_GENERAL_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 %d\n", vk_offset)); + talloc_free(vk); + return WERR_GENERAL_FAILURE; + } + + /* FIXME: name character set ?*/ + if (name != NULL) + *name = talloc_strndup(ctx, vk->data_name, vk->name_length); + + if (data_type != NULL) + *data_type = vk->data_type; + + if (vk->data_length & 0x80000000) { + vk->data_length &=~0x80000000; + data->data = (uint8_t *)&vk->data_offset; + data->length = vk->data_length; + } 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) +{ + 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_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; + + data = hbin_get(private_data->hive, nk->subkeys_offset); + if (!data.data) { + DEBUG(0, ("Unable to find subkey list\n")); + return WERR_GENERAL_FAILURE; + } + + if (!strncmp((char *)data.data, "li", 2)) { + struct li_block li; + struct tdr_pull pull; + + DEBUG(10, ("Subkeys in LI list\n")); + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_li_block(&pull, nk, &li))) { + DEBUG(0, ("Error parsing LI list\n")); + return WERR_GENERAL_FAILURE; + } + SMB_ASSERT(!strncmp(li.header, "li", 2)); + + if (li.key_count != nk->num_subkeys) { + DEBUG(0, ("Subkey counts don't match\n")); + return WERR_GENERAL_FAILURE; + } + key_off = li.nk_offset[idx]; + + } else if (!strncmp((char *)data.data, "lf", 2)) { + struct lf_block lf; + struct tdr_pull pull; + + DEBUG(10, ("Subkeys in LF list\n")); + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_lf_block(&pull, nk, &lf))) { + DEBUG(0, ("Error parsing LF list\n")); + return WERR_GENERAL_FAILURE; + } + SMB_ASSERT(!strncmp(lf.header, "lf", 2)); + + if (lf.key_count != nk->num_subkeys) { + DEBUG(0, ("Subkey counts don't match\n")); + return WERR_GENERAL_FAILURE; + } + + key_off = lf.hr[idx].nk_offset; + } else if (!strncmp((char *)data.data, "lh", 2)) { + struct lh_block lh; + struct tdr_pull pull; + + DEBUG(10, ("Subkeys in LH list\n")); + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, nk, &lh))) { + DEBUG(0, ("Error parsing LH list\n")); + return WERR_GENERAL_FAILURE; + } + SMB_ASSERT(!strncmp(lh.header, "lh", 2)); + + if (lh.key_count != nk->num_subkeys) { + DEBUG(0, ("Subkey counts don't match\n")); + return WERR_GENERAL_FAILURE; + } + key_off = lh.hr[idx].nk_offset; + } else if (!strncmp((char *)data.data, "ri", 2)) { + struct ri_block ri; + struct tdr_pull pull; + uint16_t i; + uint16_t sublist_count = 0; + + DEBUG(10, ("Subkeys in RI list\n")); + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_ri_block(&pull, nk, &ri))) { + DEBUG(0, ("Error parsing RI list\n")); + return WERR_GENERAL_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.")); + return WERR_GENERAL_FAILURE; + } + + ZERO_STRUCT(pull); + 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")); + return WERR_GENERAL_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")); + return WERR_GENERAL_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")); + + return WERR_GENERAL_FAILURE; + } + + } + + 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_GENERAL_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); + } 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_GENERAL_FAILURE; + } + + ZERO_STRUCT(pull); + pull.data = subkey_data; + + if (NT_STATUS_IS_ERR(tdr_pull_nk_block(&pull, ctx, &subkey))) { + DEBUG(0, ("Error parsing NK structure.\n")); + return WERR_GENERAL_FAILURE; + } + + if (strncmp(subkey.header, "nk", 2)) { + DEBUG(0, ("Not an NK structure.\n")); + return WERR_GENERAL_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; + + data = hbin_get(private_data->hive, nk->subkeys_offset); + if (!data.data) { + DEBUG(0, ("Unable to find subkey list\n")); + return WERR_GENERAL_FAILURE; + } + + if (!strncmp((char *)data.data, "li", 2)) { + struct li_block li; + struct tdr_pull pull; + uint16_t i; + + DEBUG(10, ("Subkeys in LI list\n")); + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_li_block(&pull, nk, &li))) { + DEBUG(0, ("Error parsing LI list\n")); + return WERR_GENERAL_FAILURE; + } + SMB_ASSERT(!strncmp(li.header, "li", 2)); + + if (li.key_count != nk->num_subkeys) { + DEBUG(0, ("Subkey counts don't match\n")); + return WERR_GENERAL_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_NOT_FOUND; + } else if (!strncmp((char *)data.data, "lf", 2)) { + struct lf_block lf; + struct tdr_pull pull; + uint16_t i; + + DEBUG(10, ("Subkeys in LF list\n")); + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_lf_block(&pull, nk, &lf))) { + DEBUG(0, ("Error parsing LF list\n")); + return WERR_GENERAL_FAILURE; + } + SMB_ASSERT(!strncmp(lf.header, "lf", 2)); + + if (lf.key_count != nk->num_subkeys) { + DEBUG(0, ("Subkey counts don't match\n")); + return WERR_GENERAL_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_NOT_FOUND; + } else if (!strncmp((char *)data.data, "lh", 2)) { + struct lh_block lh; + struct tdr_pull pull; + uint16_t i; + uint32_t hash; + + DEBUG(10, ("Subkeys in LH list\n")); + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, nk, &lh))) { + DEBUG(0, ("Error parsing LH list\n")); + return WERR_GENERAL_FAILURE; + } + SMB_ASSERT(!strncmp(lh.header, "lh", 2)); + + if (lh.key_count != nk->num_subkeys) { + DEBUG(0, ("Subkey counts don't match\n")); + return WERR_GENERAL_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_NOT_FOUND; + } else if (!strncmp((char *)data.data, "ri", 2)) { + struct ri_block ri; + struct tdr_pull pull; + uint16_t i, j; + + DEBUG(10, ("Subkeys in RI list\n")); + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_ri_block(&pull, nk, &ri))) { + DEBUG(0, ("Error parsing RI list\n")); + return WERR_GENERAL_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.")); + return WERR_GENERAL_FAILURE; + } + + ZERO_STRUCT(pull); + 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")); + return WERR_GENERAL_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")); + return WERR_GENERAL_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; + } + if (!key_off) + return WERR_NOT_FOUND; + } else { + DEBUG(0, ("Unknown subkey list type.\n")); + return WERR_GENERAL_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 (NT_STATUS_IS_ERR(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_GENERAL_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_BADFILE; + } + /* 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_BADFILE; + } + /* 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_BADFILE; + } + /* 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_BADFILE; + } + 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_GENERAL_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_BADFILE; + } + /* 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_BADFILE; + } + /* 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_GENERAL_FAILURE; + } + + if (strcmp(sk.header, "sk") != 0) { + DEBUG(0, ("Expected 'sk', got '%s'\n", sk.header)); + return WERR_GENERAL_FAILURE; + } + + *sd = talloc(ctx, struct security_descriptor); + W_ERROR_HAVE_NO_MEMORY(*sd); + + data.data = sk.sec_desc; + data.length = sk.rec_size; + if (NT_STATUS_IS_ERR(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_GENERAL_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_BADFILE; + } + + if (!strncmp((char *)data.data, "li", 2)) { + struct tdr_pull pull; + struct li_block li; + + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_li_block(&pull, regf, &li))) { + DEBUG(0, ("Error parsing LI list\n")); + return WERR_BADFILE; + } + + if (strncmp(li.header, "li", 2) != 0) { + abort(); + DEBUG(0, ("LI header corrupt\n")); + return WERR_BADFILE; + } + + li.nk_offset = talloc_realloc(regf, li.nk_offset, uint32_t, li.key_count+1); + W_ERROR_HAVE_NO_MEMORY(li.nk_offset); + li.nk_offset[li.key_count] = 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; + struct lf_block lf; + + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_lf_block(&pull, regf, &lf))) { + DEBUG(0, ("Error parsing LF list\n")); + return WERR_BADFILE; + } + SMB_ASSERT(!strncmp(lf.header, "lf", 2)); + + lf.hr = talloc_realloc(regf, lf.hr, struct hash_record, lf.key_count+1); + W_ERROR_HAVE_NO_MEMORY(lf.hr); + lf.hr[lf.key_count].nk_offset = key_offset; + lf.hr[lf.key_count].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; + struct lh_block lh; + + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, regf, &lh))) { + DEBUG(0, ("Error parsing LH list\n")); + return WERR_BADFILE; + } + SMB_ASSERT(!strncmp(lh.header, "lh", 2)); + + lh.hr = talloc_realloc(regf, lh.hr, struct lh_hash, lh.key_count+1); + W_ERROR_HAVE_NO_MEMORY(lh.hr); + lh.hr[lh.key_count].nk_offset = key_offset; + lh.hr[lh.key_count].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_BADFILE; + } + + 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_BADFILE; + } + + if (strncmp((char *)data.data, "li", 2) == 0) { + struct li_block li; + struct tdr_pull pull; + uint16_t i; + bool found_offset = false; + + DEBUG(10, ("Subkeys in LI list\n")); + + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_li_block(&pull, regf, &li))) { + DEBUG(0, ("Error parsing LI list\n")); + return WERR_BADFILE; + } + + 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(0, ("Subkey not found\n")); + return WERR_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; + uint16_t i; + bool found_offset = false; + + DEBUG(10, ("Subkeys in LF list\n")); + + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_lf_block(&pull, regf, &lf))) { + DEBUG(0, ("Error parsing LF list\n")); + return WERR_BADFILE; + } + + 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(0, ("Subkey not found\n")); + return WERR_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; + uint16_t i; + bool found_offset = false; + + DEBUG(10, ("Subkeys in LH list\n")); + + ZERO_STRUCT(pull); + pull.data = data; + + if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, regf, &lh))) { + DEBUG(0, ("Error parsing LF list\n")); + return WERR_BADFILE; + } + + 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_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_BADFILE; + } + return WERR_OK; +} + +static WERROR regf_del_value (struct hive_key *key, const char *name) +{ + const struct regf_key_data *private_data = + (const 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; + uint32_t i; + + if (nk->values_offset == -1) { + return WERR_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_BADFILE; + } + if (strcmp(vk.data_name, name) == 0) { + hbin_free(regf, vk_offset); + found_offset = true; + } + } + } + if (!found_offset) { + return WERR_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); +} + + +static WERROR regf_del_key(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_NOT_FOUND; + } + + /* Find the key */ + if (!W_ERROR_IS_OK(regf_get_subkey_by_name(parent_nk, parent, name, + (struct hive_key **)&key))) { + DEBUG(0, ("Key '%s' not found\n", name)); + return WERR_NOT_FOUND; + } + + if (key->nk->subkeys_offset != -1 || + key->nk->values_offset != -1) { + DEBUG(0, ("Key '%s' is not empty.\n", name)); + return WERR_FILE_EXISTS; + } + + /* 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); +} + +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, 5); + 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 %d\n", offset)); + return WERR_GENERAL_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); + + return regf_save_hbin(private_data->hive); +} + +static WERROR regf_set_value(struct hive_key *key, const char *name, + uint32_t type, const DATA_BLOB data) +{ + const struct regf_key_data *private_data = + (const 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 = -1; + DATA_BLOB values; + + 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 %d\n", tmp_vk_offset)); + return WERR_GENERAL_FAILURE; + } + if (strcmp(vk.data_name, name) == 0) { + old_vk_offset = tmp_vk_offset; + break; + } + } + /* Free data, if any */ + if (!(vk.data_length & 0x80000000)) { + hbin_free(regf, vk.data_offset); + } + } + if (old_vk_offset == -1) { + vk.header = "vk"; + vk.name_length = strlen(name); + if (name != NULL && name[0] != 0) { + vk.flag = 1; + vk.data_name = name; + } else { + vk.data_name = NULL; + vk.flag = 0; + } + } + /* Set the type and data */ + vk.data_length = data.length; + vk.data_type = type; + if (type == REG_DWORD) { + vk.data_length |= 0x80000000; + vk.data_offset = *(uint32_t *)data.data; + } 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 = (void *)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); +} + +static WERROR regf_save_hbin(struct regf_data *regf) +{ + struct tdr_push *push = talloc_zero(regf, struct tdr_push); + int i; + + W_ERROR_HAVE_NO_MEMORY(push); + + if (lseek(regf->fd, 0, SEEK_SET) == -1) { + DEBUG(0, ("Error lseeking in regf file\n")); + return WERR_GENERAL_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_GENERAL_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_GENERAL_FAILURE; + } + + if (lseek(regf->fd, 0x1000, SEEK_SET) == -1) { + DEBUG(0, ("Error lseeking to 0x1000 in regf file\n")); + return WERR_GENERAL_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_GENERAL_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 tdr_pull pull; + int i; + struct nk_block nk; + WERROR error; + + 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_GENERAL_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; + + pull.offset = 0x1000; + + i = 0; + /* Create all hbin blocks */ + regf->hbins = talloc_array(regf, struct hbin_block *, 1); + W_ERROR_HAVE_NO_MEMORY(regf->hbins); + regf->hbins[0] = NULL; + + regf_hdr->data_offset = -1; /* FIXME */ + + nk.header = "nk"; + nk.type = REG_SUB_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); + nk.clsname_offset = -1; /* FIXME: fill in */ + nk.clsname_length = 0; + nk.key_name = ""; + + nk.sk_offset = -1; /* FIXME: fill in */ + + /* Store the new nk key */ + regf->header->data_offset = hbin_store_tdr(regf, + (tdr_push_fn_t)tdr_push_nk_block, &nk); + + *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_free(regf); + + error = regf_save_hbin(regf); + if (!W_ERROR_IS_OK(error)) { + return error; + } + + return WERR_OK; +} + +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; + int i; + + regf = (struct regf_data *)talloc_zero(NULL, struct regf_data); + + W_ERROR_HAVE_NO_MEMORY(regf); + + 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_GENERAL_FAILURE; + } + + ZERO_STRUCT(pull); + pull.data.data = (uint8_t*)fd_load(regf->fd, &pull.data.length, regf); + + if (pull.data.data == NULL) { + DEBUG(0, ("Error reading data\n")); + talloc_free(regf); + return WERR_GENERAL_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))) { + talloc_free(regf); + return WERR_GENERAL_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_GENERAL_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_GENERAL_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; + } + + 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_free(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, +}; diff --git a/source4/lib/registry/regf.idl b/source4/lib/registry/regf.idl index 46af4ffdc5..ffd7072b7a 100644 --- a/source4/lib/registry/regf.idl +++ b/source4/lib/registry/regf.idl @@ -45,8 +45,8 @@ interface regf uint32 data_offset; uint32 last_block; [value(1)] uint32 uk7; /* 1 */ - [charset(UTF16)] uint16 description[0x40]; - uint32 padding[83]; /* Padding */ + [charset(UTF16)] uint16 description[0x20]; + uint32 padding[99]; /* Padding */ /* Checksum of first 0x200 bytes XOR-ed */ uint32 chksum; }; @@ -66,7 +66,7 @@ interface regf uint8 data[offset_to_next-0x20]; /* data is filled with: uint32 length; - Negative if in used, positive otherwise + Negative if in use, positive otherwise Always a multiple of 8 uint8_t data[length]; Free space marker if 0xffffffff diff --git a/source4/lib/registry/registry.h b/source4/lib/registry/registry.h index 09d61c6b4f..acfe056616 100644 --- a/source4/lib/registry/registry.h +++ b/source4/lib/registry/registry.h @@ -2,7 +2,7 @@ Unix SMB/CIFS implementation. Registry interface Copyright (C) Gerald Carter 2002. - Copyright (C) Jelmer Vernooij 2003-2004. + 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 @@ -21,9 +21,12 @@ #ifndef _REGISTRY_H /* _REGISTRY_H */ #define _REGISTRY_H +struct registry_context; + #include "core.h" #include "talloc/talloc.h" #include "librpc/gen_ndr/security.h" +#include "lib/registry/hive.h" /* Handles for the predefined keys */ #define HKEY_CLASSES_ROOT 0x80000000 @@ -36,6 +39,9 @@ #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; @@ -52,17 +58,16 @@ extern const struct reg_predefined_key reg_predefined_keys[]; #define REGISTRY_INTERFACE_VERSION 1 +struct reg_key_operations; + /* structure to store the registry handles */ struct registry_key { - const char *name; - const char *path; - const char *class_name; - NTTIME last_mod; - struct registry_hive *hive; - void *backend_data; + struct registry_context *context; }; +#include "lib/registry/patchfile.h" + struct registry_value { const char *name; @@ -74,109 +79,87 @@ struct registry_value typedef void (*reg_key_notification_function) (void); typedef void (*reg_value_notification_function) (void); -/* - * Container for function pointers to enumeration routines - * for virtual registry view - * - * Backends can provide : - * - just one hive (example: nt4, w95) - * - several hives (example: rpc). - * - * Backends should always do case-insensitive compares - * (everything is case-insensitive but case-preserving, - * just like the FS) - * - * There is no save function as all operations are expected to - * be atomic. - */ - -struct hive_operations { - const char *name; - - /* Implement this one */ - WERROR (*open_hive) (struct registry_hive *, struct registry_key **); - - /* Or this one */ - WERROR (*open_key) (TALLOC_CTX *, const struct registry_key *, const char *name, struct registry_key **); - - WERROR (*num_subkeys) (const struct registry_key *, uint32_t *count); - WERROR (*num_values) (const struct registry_key *, uint32_t *count); - WERROR (*get_subkey_by_index) (TALLOC_CTX *, const struct registry_key *, int idx, struct registry_key **); - - /* Can not contain more than one level */ - WERROR (*get_subkey_by_name) (TALLOC_CTX *, const struct registry_key *, const char *name, struct registry_key **); - WERROR (*get_value_by_index) (TALLOC_CTX *, const struct registry_key *, int idx, struct registry_value **); - - /* Can not contain more than one level */ - WERROR (*get_value_by_name) (TALLOC_CTX *, const struct registry_key *, const char *name, struct registry_value **); - - /* Security control */ - WERROR (*key_get_sec_desc) (TALLOC_CTX *, const struct registry_key *, struct security_descriptor **); - WERROR (*key_set_sec_desc) (const struct registry_key *, const struct security_descriptor *); - - /* Notification */ - WERROR (*request_key_change_notify) (const struct registry_key *, reg_key_notification_function); - WERROR (*request_value_change_notify) (const struct registry_value *, reg_value_notification_function); - - /* Key management */ - WERROR (*add_key)(TALLOC_CTX *, const struct registry_key *, const char *name, uint32_t access_mask, struct security_descriptor *, struct registry_key **); - WERROR (*del_key)(const struct registry_key *, const char *name); - WERROR (*flush_key) (const struct registry_key *); - - /* Value management */ - WERROR (*set_value)(const struct registry_key *, const char *name, uint32_t type, const DATA_BLOB data); - WERROR (*del_value)(const struct registry_key *, const char *valname); -}; - struct cli_credentials; +struct registry_context; -struct registry_hive -{ - const struct hive_operations *functions; - struct registry_key *root; - struct auth_session_info *session_info; - struct cli_credentials *credentials; - void *backend_data; - const char *location; -}; - -/* Handle to a full registry - * contains zero or more hives */ -struct registry_context { - void *backend_data; - struct cli_credentials *credentials; - struct auth_session_info *session_info; - WERROR (*get_predefined_key) (struct registry_context *, uint32_t hkey, struct registry_key **); -}; - -struct reg_init_function_entry { - const struct hive_operations *hive_functions; - struct reg_init_function_entry *prev, *next; -}; - -/* Representing differences between registry files */ - -struct reg_diff_value -{ +struct registry_operations { const char *name; - enum { REG_DIFF_DEL_VAL, REG_DIFF_SET_VAL } changetype; - uint32_t type; - DATA_BLOB data; -}; -struct reg_diff_key -{ - const char *name; - enum { REG_DIFF_CHANGE_KEY, REG_DIFF_DEL_KEY } changetype; - uint32_t numvalues; - struct reg_diff_value *values; -}; - -struct reg_diff -{ - const char *format; - uint32_t numkeys; - struct reg_diff_key *keys; + 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); + + WERROR (*flush_key) (struct registry_key *key); + + WERROR (*get_predefined_key) (const 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) (struct registry_key *key, const char *name); + + WERROR (*delete_value) (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_security) (TALLOC_CTX *mem_ctx, + const struct registry_key *key, + struct security_descriptor **security); + + WERROR (*set_security) (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; @@ -186,60 +169,110 @@ struct event_context; #define _PUBLIC_ #endif +/** + * Open the locally defined registry. + */ _PUBLIC_ WERROR reg_open_local (TALLOC_CTX *mem_ctx, struct registry_context **ctx, struct auth_session_info *session_info, struct cli_credentials *credentials); +_PUBLIC_ WERROR reg_open_samba (TALLOC_CTX *mem_ctx, + struct registry_context **ctx, + struct auth_session_info *session_info, + struct cli_credentials *credentials); + +/** + * Open the registry on a remote machine. + */ _PUBLIC_ WERROR reg_open_remote(struct registry_context **ctx, struct auth_session_info *session_info, struct cli_credentials *credentials, const char *location, struct event_context *ev); -_PUBLIC_ NTSTATUS registry_register(const void *_hive_ops); -_PUBLIC_ NTSTATUS registry_init(void); -_PUBLIC_ BOOL reg_has_backend(const char *backend); -_PUBLIC_ int reg_list_predefs(TALLOC_CTX *mem_ctx, char ***predefs, uint32_t **hkeys); +_PUBLIC_ WERROR reg_open_wine(struct registry_context **ctx, const char *path); + _PUBLIC_ const char *reg_get_predef_name(uint32_t hkey); -_PUBLIC_ WERROR reg_get_predefined_key_by_name(struct registry_context *ctx, const char *name, struct registry_key **key); -_PUBLIC_ WERROR reg_get_predefined_key(struct registry_context *ctx, uint32_t hkey, struct registry_key **key); -_PUBLIC_ WERROR reg_open_hive(TALLOC_CTX *parent_ctx, const char *backend, const char *location, struct auth_session_info *session_info, struct cli_credentials *credentials, struct registry_key **root); -_PUBLIC_ WERROR reg_open_key(TALLOC_CTX *mem_ctx, struct registry_key *parent, const char *name, struct registry_key **result); -_PUBLIC_ WERROR reg_key_get_value_by_index(TALLOC_CTX *mem_ctx, const struct registry_key *key, int idx, struct registry_value **val); -_PUBLIC_ WERROR reg_key_num_subkeys(const struct registry_key *key, uint32_t *count); -_PUBLIC_ WERROR reg_key_num_values(const struct registry_key *key, uint32_t *count); -_PUBLIC_ WERROR reg_key_get_subkey_by_index(TALLOC_CTX *mem_ctx, const struct registry_key *key, int idx, struct registry_key **subkey); -WERROR reg_key_get_subkey_by_name(TALLOC_CTX *mem_ctx, const struct registry_key *key, const char *name, struct registry_key **subkey); -_PUBLIC_ WERROR reg_key_get_value_by_name(TALLOC_CTX *mem_ctx, const struct registry_key *key, const char *name, struct registry_value **val); +_PUBLIC_ WERROR reg_get_predefined_key_by_name(struct registry_context *ctx, + const char *name, + struct registry_key **key); +_PUBLIC_ WERROR reg_get_predefined_key(const struct registry_context *ctx, + uint32_t hkey, + struct registry_key **key); + +_PUBLIC_ WERROR reg_open_key(TALLOC_CTX *mem_ctx, struct registry_key *parent, + const char *name, struct registry_key **result); + +_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); +_PUBLIC_ 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); +_PUBLIC_ WERROR reg_key_get_subkey_by_index(TALLOC_CTX *mem_ctx, + const struct registry_key *key, + int 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); +_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); _PUBLIC_ WERROR reg_key_del(struct registry_key *parent, const char *name); -_PUBLIC_ WERROR reg_key_add_name(TALLOC_CTX *mem_ctx, const struct registry_key *parent, const char *name, uint32_t access_mask, struct security_descriptor *desc, struct registry_key **newkey); -_PUBLIC_ WERROR reg_val_set(struct registry_key *key, const char *value, uint32_t type, DATA_BLOB data); +_PUBLIC_ 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); +_PUBLIC_ WERROR reg_val_set(struct registry_key *key, const char *value, + uint32_t type, DATA_BLOB data); _PUBLIC_ WERROR reg_get_sec_desc(TALLOC_CTX *ctx, const struct registry_key *key, struct security_descriptor **secdesc); -_PUBLIC_ WERROR reg_del_value(const struct registry_key *key, const char *valname); -_PUBLIC_ WERROR reg_key_flush(const struct registry_key *key); -_PUBLIC_ WERROR reg_key_subkeysizes(const struct registry_key *key, uint32_t *max_subkeylen, uint32_t *max_subkeysize); -_PUBLIC_ WERROR reg_key_valuesizes(const struct registry_key *key, uint32_t *max_valnamelen, uint32_t *max_valbufsize); +_PUBLIC_ WERROR reg_del_value(struct registry_key *key, const char *valname); +_PUBLIC_ WERROR reg_key_flush(struct registry_key *key); +WERROR reg_create_key (TALLOC_CTX *mem_ctx, + struct registry_key *parent, -/* Utility functions */ + const char *name, + const char *key_class, + struct security_descriptor *security, + struct registry_key **key); + + + +/* Utility functions */ _PUBLIC_ const char *str_regtype(int type); -_PUBLIC_ char *reg_val_data_string(TALLOC_CTX *mem_ctx, uint32_t type, DATA_BLOB *data); -_PUBLIC_ char *reg_val_description(TALLOC_CTX *mem_ctx, struct registry_value *val) ; +_PUBLIC_ char *reg_val_data_string(TALLOC_CTX *mem_ctx, uint32_t type, + const DATA_BLOB data); +_PUBLIC_ char *reg_val_description(TALLOC_CTX *mem_ctx, const char *name, + uint32_t type, const DATA_BLOB data); _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 *reg_path_win2unix(char *path) ; -char *reg_path_unix2win(char *path) ; 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); -/* Patch files */ - -_PUBLIC_ struct reg_diff *reg_generate_diff(TALLOC_CTX *mem_ctx, struct registry_context *ctx1, struct registry_context *ctx2); -_PUBLIC_ WERROR reg_diff_save(const struct reg_diff *diff, const char *filename); -_PUBLIC_ struct reg_diff *reg_diff_load(TALLOC_CTX *ctx, const char *fn); -_PUBLIC_ BOOL reg_diff_apply (const struct reg_diff *diff, struct registry_context *ctx); +struct registry_key *reg_import_hive_key(struct registry_context *ctx, + struct hive_key *hive, + uint32_t predef_key, + const char **elements); -NTSTATUS registry_rpc_init(void); #endif /* _REGISTRY_H */ diff --git a/source4/lib/registry/reg_backend_rpc.c b/source4/lib/registry/rpc.c index 50489aced2..59d41d591a 100644 --- a/source4/lib/registry/reg_backend_rpc.c +++ b/source4/lib/registry/rpc.c @@ -1,7 +1,7 @@ /* Samba Unix/Linux SMB implementation RPC backend for the registry library - Copyright (C) 2003-2004 Jelmer Vernooij, jelmer@samba.org + Copyright (C) 2003-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 @@ -20,7 +20,23 @@ #include "registry.h" #include "librpc/gen_ndr/ndr_winreg_c.h" -static struct hive_operations reg_backend_rpc; +struct rpc_key { + struct registry_key key; + struct policy_handle pol; + struct dcerpc_pipe *pipe; + + uint32_t num_values; + uint32_t num_subkeys; + uint32_t max_valnamelen; + uint32_t max_valdatalen; +}; + +struct rpc_registry_context { + struct registry_context context; + struct dcerpc_pipe *pipe; +}; + +static struct registry_operations reg_backend_rpc; /** * This is the RPC backend for the registry library. @@ -58,57 +74,46 @@ openhive(HKCR) openhive(HKDD) openhive(HKCC) -struct rpc_key_data { - struct policy_handle pol; - int num_subkeys; - int num_values; - int max_valnamelen; - int max_valdatalen; -}; - static struct { uint32_t hkey; WERROR (*open) (struct dcerpc_pipe *p, 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 } + { 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(const struct registry_key *k); -static WERROR rpc_get_predefined_key (struct registry_context *ctx, uint32_t hkey_type, 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 registry_hive *h; - struct rpc_key_data *mykeydata; + struct rpc_registry_context *rctx = talloc_get_type(ctx, + struct rpc_registry_context); + struct rpc_key *mykeydata; - for(n = 0; known_hives[n].hkey; n++) - { - if(known_hives[n].hkey == hkey_type) break; + for(n = 0; known_hives[n].hkey; n++) { + if(known_hives[n].hkey == hkey_type) + break; } - if(!known_hives[n].open) { + if (known_hives[n].open == NULL) { DEBUG(1, ("No such hive %d\n", hkey_type)); return WERR_NO_MORE_ITEMS; } - h = talloc(ctx, struct registry_hive); - h->functions = ®_backend_rpc; - h->location = NULL; - h->backend_data = ctx->backend_data; - - (*k) = h->root = talloc(h, struct registry_key); - (*k)->hive = h; - (*k)->backend_data = mykeydata = talloc(*k, struct rpc_key_data); + mykeydata = talloc(ctx, struct rpc_key); + mykeydata->pipe = rctx->pipe; mykeydata->num_values = -1; mykeydata->num_subkeys = -1; - return known_hives[n].open((struct dcerpc_pipe *)ctx->backend_data, *k, &(mykeydata->pol)); + return known_hives[n].open(mykeydata->pipe, *k, &(mykeydata->pol)); } #if 0 @@ -136,44 +141,44 @@ static WERROR rpc_key_put_rpc_data(TALLOC_CTX *mem_ctx, struct registry_key *k) } #endif -static WERROR rpc_open_key(TALLOC_CTX *mem_ctx, const struct registry_key *h, const char *name, struct registry_key **key) +static WERROR rpc_open_key(TALLOC_CTX *mem_ctx, struct registry_key *h, + const char *name, struct registry_key **key) { - struct rpc_key_data *mykeydata; + struct rpc_key *mykeydata = talloc_get_type(h, struct rpc_key), + *newkeydata; struct winreg_OpenKey r; - *key = talloc(mem_ctx, struct registry_key); - (*key)->name = talloc_strdup(mem_ctx, name); - - (*key)->backend_data = mykeydata = talloc(mem_ctx, struct rpc_key_data); - mykeydata->num_values = -1; - mykeydata->num_subkeys = -1; + mykeydata = talloc(mem_ctx, struct rpc_key); /* Then, open the handle using the hive */ - memset(&r, 0, sizeof(struct winreg_OpenKey)); - r.in.parent_handle = &(((struct rpc_key_data *)h->backend_data)->pol); + r.in.parent_handle = &mykeydata->pol; init_winreg_String(&r.in.keyname, name); r.in.unknown = 0x00000000; r.in.access_mask = 0x02000000; - r.out.handle = &mykeydata->pol; + r.out.handle = &newkeydata->pol; - dcerpc_winreg_OpenKey((struct dcerpc_pipe *)(h->hive->backend_data), mem_ctx, &r); + dcerpc_winreg_OpenKey(mykeydata->pipe, mem_ctx, &r); return r.out.result; } -static WERROR rpc_get_value_by_index(TALLOC_CTX *mem_ctx, const struct registry_key *parent, int n, struct registry_value **value) +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_data *mykeydata = parent->backend_data; + struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key); WERROR error; struct winreg_EnumValue r; uint32_t len1, zero = 0; - enum winreg_Type type; NTSTATUS status; struct winreg_StringBuf name; uint8_t u8; - if(mykeydata->num_values == -1) { + if (mykeydata->num_values == -1) { error = rpc_query_key(parent); if(!W_ERROR_IS_OK(error)) return error; } @@ -187,13 +192,13 @@ static WERROR rpc_get_value_by_index(TALLOC_CTX *mem_ctx, const struct registry_ r.in.handle = &mykeydata->pol; r.in.enum_index = n; r.in.name = &name; - r.in.type = &type; + r.in.type = type; r.in.value = &u8; r.in.length = &zero; r.in.size = &len1; r.out.name = &name; - status = dcerpc_winreg_EnumValue((struct dcerpc_pipe *)parent->hive->backend_data, mem_ctx, &r); + status = dcerpc_winreg_EnumValue(mykeydata->pipe, mem_ctx, &r); if(NT_STATUS_IS_ERR(status)) { DEBUG(0, ("Error in EnumValue: %s\n", nt_errstr(status))); return WERR_GENERAL_FAILURE; @@ -201,20 +206,23 @@ static WERROR rpc_get_value_by_index(TALLOC_CTX *mem_ctx, const struct registry_ if(NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result) && r.out.length) { - *value = talloc(mem_ctx, struct registry_value); - (*value)->name = talloc_strdup(mem_ctx, r.out.name->name); - (*value)->data_type = type; - (*value)->data = data_blob_talloc(mem_ctx, r.out.value, *r.out.length); + *value_name = talloc_strdup(mem_ctx, r.out.name->name); + *data = data_blob_talloc(mem_ctx, r.out.value, *r.out.length); return WERR_OK; } return r.out.result; } -static WERROR rpc_get_subkey_by_index(TALLOC_CTX *mem_ctx, const struct registry_key *parent, int n, struct registry_key **subkey) +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_data *mykeydata = parent->backend_data; + struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key); NTSTATUS status; struct winreg_StringBuf namebuf, classbuf; NTTIME change_time = 0; @@ -233,40 +241,47 @@ static WERROR rpc_get_subkey_by_index(TALLOC_CTX *mem_ctx, const struct registry r.in.last_changed_time = &change_time; r.out.name = &namebuf; - status = dcerpc_winreg_EnumKey((struct dcerpc_pipe *)parent->hive->backend_data, mem_ctx, &r); + status = dcerpc_winreg_EnumKey(mykeydata->pipe, mem_ctx, &r); if(NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)) { - char *name = talloc_strdup(mem_ctx, r.out.name->name); - return rpc_open_key(mem_ctx, parent, name, subkey); + *name = talloc_strdup(mem_ctx, r.out.name->name); + *keyclass = talloc_strdup(mem_ctx, r.out.keyclass->name); + *last_changed_time = *r.out.last_changed_time; } return r.out.result; } -static WERROR rpc_add_key(TALLOC_CTX *mem_ctx, const struct registry_key *parent, const char *name, uint32_t access_mask, struct security_descriptor *sec, struct registry_key **key) +static WERROR rpc_add_key(TALLOC_CTX *mem_ctx, + struct registry_key *parent, const char *name, + const char *key_class, + struct security_descriptor *sec, + struct registry_key **key) { NTSTATUS status; struct winreg_CreateKey r; + struct rpc_key *parentkd = talloc_get_type(parent, struct rpc_key); + struct rpc_key *rpck = talloc(mem_ctx, struct rpc_key); init_winreg_String(&r.in.name, name); init_winreg_String(&r.in.keyclass, NULL); - r.in.handle = parent->backend_data; - r.out.new_handle = talloc(mem_ctx, struct policy_handle); + r.in.handle = &parentkd->pol; + r.out.new_handle = &rpck->pol; r.in.options = 0; - r.in.access_mask = access_mask; + r.in.access_mask = SEC_STD_ALL; r.in.secdesc = NULL; - status = dcerpc_winreg_CreateKey((struct dcerpc_pipe *)(parent->hive->backend_data), mem_ctx, &r); + status = dcerpc_winreg_CreateKey(parentkd->pipe, 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); } if (W_ERROR_IS_OK(r.out.result)) { - *key = talloc(mem_ctx, struct registry_key); - (*key)->name = talloc_strdup(*key, name); - (*key)->backend_data = r.out.new_handle; + rpck->pipe = talloc_reference(rpck, parentkd->pipe); + *key = (struct registry_key *)rpck; } return r.out.result; @@ -276,14 +291,14 @@ static WERROR rpc_query_key(const struct registry_key *k) { NTSTATUS status; struct winreg_QueryInfoKey r; - struct rpc_key_data *mykeydata = k->backend_data; + struct rpc_key *mykeydata = talloc_get_type(k, struct rpc_key); TALLOC_CTX *mem_ctx = talloc_init("query_key"); r.in.classname = talloc(mem_ctx, struct winreg_String); init_winreg_String(r.in.classname, NULL); r.in.handle = &mykeydata->pol; - status = dcerpc_winreg_QueryInfoKey((struct dcerpc_pipe *)(k->hive->backend_data), mem_ctx, &r); + status = dcerpc_winreg_QueryInfoKey(mykeydata->pipe, mem_ctx, &r); talloc_free(mem_ctx); if (!NT_STATUS_IS_OK(status)) { @@ -301,26 +316,29 @@ static WERROR rpc_query_key(const struct registry_key *k) return r.out.result; } -static WERROR rpc_del_key(const struct registry_key *parent, const char *name) +static WERROR rpc_del_key(struct registry_key *parent, const char *name) { NTSTATUS status; - struct rpc_key_data *mykeydata = parent->backend_data; + struct rpc_key *mykeydata = talloc_get_type(parent, struct rpc_key); struct winreg_DeleteKey r; TALLOC_CTX *mem_ctx = talloc_init("del_key"); r.in.handle = &mykeydata->pol; init_winreg_String(&r.in.key, name); - status = dcerpc_winreg_DeleteKey((struct dcerpc_pipe *)parent->hive->backend_data, mem_ctx, &r); + status = dcerpc_winreg_DeleteKey(mykeydata->pipe, mem_ctx, &r); talloc_free(mem_ctx); return r.out.result; } -static WERROR rpc_num_values(const struct registry_key *key, uint32_t *count) +static WERROR rpc_get_info(TALLOC_CTX *mem_ctx, const struct registry_key *key, + const char **classname, + uint32_t *numsubkeys, + uint32_t *numvalue) { - struct rpc_key_data *mykeydata = key->backend_data; + struct rpc_key *mykeydata = talloc_get_type(key, struct rpc_key); WERROR error; if(mykeydata->num_values == -1) { @@ -328,42 +346,34 @@ static WERROR rpc_num_values(const struct registry_key *key, uint32_t *count) if(!W_ERROR_IS_OK(error)) return error; } - *count = mykeydata->num_values; + /* FIXME: *classname = talloc_strdup(mem_ctx, mykeydata->classname); */ + *numvalue = mykeydata->num_values; + *numsubkeys = mykeydata->num_subkeys; return WERR_OK; } -static WERROR rpc_num_subkeys(const struct registry_key *key, uint32_t *count) -{ - struct rpc_key_data *mykeydata = key->backend_data; - WERROR error; - - if(mykeydata->num_subkeys == -1) { - error = rpc_query_key(key); - if(!W_ERROR_IS_OK(error)) return error; - } - - *count = mykeydata->num_subkeys; - return WERR_OK; -} - -static struct hive_operations reg_backend_rpc = { +static struct registry_operations reg_backend_rpc = { .name = "rpc", .open_key = rpc_open_key, - .get_subkey_by_index = rpc_get_subkey_by_index, - .get_value_by_index = rpc_get_value_by_index, - .add_key = rpc_add_key, - .del_key = rpc_del_key, - .num_subkeys = rpc_num_subkeys, - .num_values = rpc_num_values, + .enum_key = rpc_get_subkey_by_index, + .enum_value = rpc_get_value_by_index, + .create_key = rpc_add_key, + .delete_key = rpc_del_key, + .get_key_info = rpc_get_info, }; -_PUBLIC_ WERROR reg_open_remote(struct registry_context **ctx, struct auth_session_info *session_info, struct cli_credentials *credentials, - const char *location, struct event_context *ev) +_PUBLIC_ WERROR reg_open_remote(struct registry_context **ctx, + struct auth_session_info *session_info, + struct cli_credentials *credentials, + const char *location, struct event_context *ev) { NTSTATUS status; struct dcerpc_pipe *p; + struct rpc_registry_context *rctx; + + dcerpc_init(); - *ctx = talloc(NULL, struct registry_context); + rctx = talloc(NULL, struct rpc_registry_context); /* Default to local smbd if no connection is specified */ if (!location) { @@ -374,7 +384,7 @@ _PUBLIC_ WERROR reg_open_remote(struct registry_context **ctx, struct auth_sessi &p, location, &ndr_table_winreg, credentials, ev); - (*ctx)->backend_data = p; + rctx->pipe = p; if(NT_STATUS_IS_ERR(status)) { DEBUG(1, ("Unable to open '%s': %s\n", location, nt_errstr(status))); @@ -383,13 +393,7 @@ _PUBLIC_ WERROR reg_open_remote(struct registry_context **ctx, struct auth_sessi return ntstatus_to_werror(status); } - (*ctx)->get_predefined_key = rpc_get_predefined_key; + *ctx = (struct registry_context *)rctx; return WERR_OK; } - -NTSTATUS registry_rpc_init(void) -{ - dcerpc_init(); - return registry_register(®_backend_rpc); -} diff --git a/source4/lib/registry/reg_samba.c b/source4/lib/registry/samba.c index 560e1ded31..244c467a2c 100644 --- a/source4/lib/registry/reg_samba.c +++ b/source4/lib/registry/samba.c @@ -1,6 +1,6 @@ /* Unix SMB/CIFS implementation. - Copyright (C) Jelmer Vernooij 2004. + 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 @@ -24,31 +24,52 @@ * @brief Samba-specific registry functions */ -static WERROR reg_samba_get_predef (struct registry_context *ctx, uint32_t hkey, struct registry_key **k) +WERROR mount_samba_hive(struct registry_context *ctx, + struct auth_session_info *auth_info, + struct cli_credentials *creds, + const char *name, + uint32_t hive_id) { WERROR error; - const char *conf; - char *backend; + struct hive_key *hive; const char *location; - const char *hivename = reg_get_predef_name(hkey); - *k = NULL; + location = talloc_asprintf(ctx, "%s/%s.ldb", lp_private_dir(), name); - conf = lp_parm_string(-1, "registry", hivename); - - if (!conf) { - return WERR_NOT_SUPPORTED; - } + error = reg_open_hive(ctx, location, auth_info, creds, &hive); + 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 auth_session_info *session_info, + struct cli_credentials *credentials) +{ + WERROR result; - location = strchr(conf, ':'); - if (location) { - backend = talloc_strndup(ctx, conf, (int)(location - conf)); - location++; - } else { - backend = talloc_strdup(ctx, "ldb"); - location = conf; + result = reg_open_local(mem_ctx, ctx, session_info, credentials); + if (!W_ERROR_IS_OK(result)) { + return result; } + mount_samba_hive(*ctx, session_info, credentials, + "hklm", HKEY_LOCAL_MACHINE); + + mount_samba_hive(*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, session_info, credentials, + "hkcu", HKEY_CURRENT_USER); + + mount_samba_hive(*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 */ @@ -59,23 +80,6 @@ static WERROR reg_samba_get_predef (struct registry_context *ctx, uint32_t hkey, /* FIXME: HKEY_LOCAL_MACHINE\Hardware is autogenerated */ /* FIXME: HKEY_LOCAL_MACHINE\Security\SAM is an alias for HKEY_LOCAL_MACHINE\SAM */ - - error = reg_open_hive(ctx, backend, location, ctx->session_info, ctx->credentials, k); - - talloc_free(backend); - - return error; -} - -_PUBLIC_ WERROR reg_open_local (TALLOC_CTX *mem_ctx, - struct registry_context **ctx, - struct auth_session_info *session_info, - struct cli_credentials *credentials) -{ - *ctx = talloc(mem_ctx, struct registry_context); - (*ctx)->credentials = talloc_reference(*ctx, credentials); - (*ctx)->session_info = talloc_reference(*ctx, session_info); - (*ctx)->get_predefined_key = reg_samba_get_predef; return WERR_OK; } diff --git a/source4/lib/registry/tests/diff.c b/source4/lib/registry/tests/diff.c new file mode 100644 index 0000000000..220da88601 --- /dev/null +++ b/source4/lib/registry/tests/diff.c @@ -0,0 +1,105 @@ +/* + Unix SMB/CIFS implementation. + + local testing of registry diff functionality + + 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/registry.h" +#include "lib/cmdline/popt_common.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/winreg.h" + +static bool test_generate_diff(struct torture_context *test) +{ + /* WERROR reg_generate_diff(struct registry_context *ctx1, + struct registry_context *ctx2, + const struct reg_diff_callbacks *callbacks, + void *callback_data) + */ + return true; +} + + +static bool test_diff_load(struct torture_context *test) +{ + /* WERROR reg_diff_load(const char *filename, const struct reg_diff_callbacks *callbacks, void *callback_data) */ + + return true; +} + +static bool test_diff_apply(struct torture_context *test) +{ + /* +_PUBLIC_ WERROR reg_diff_apply (const char *filename, struct registry_context *ctx) + */ + + 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 *test) +{ + 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, test))) + return false; + + torture_assert_str_equal(test, added_key, "bla", "key added"); + + return true; +} + +static bool test_generate_diff_key_null(struct torture_context *test) +{ + 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; +} + +struct torture_suite *torture_registry_diff(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, + "DIFF"); + torture_suite_add_simple_test(suite, "test_generate_diff_key_add", test_generate_diff_key_add); + torture_suite_add_simple_test(suite, "test_generate_diff_key_null", test_generate_diff_key_null); + torture_suite_add_simple_test(suite, "test_diff_apply", test_diff_apply); + torture_suite_add_simple_test(suite, "test_generate_diff", test_generate_diff); + torture_suite_add_simple_test(suite, "test_diff_load", test_diff_load); + return suite; +} diff --git a/source4/lib/registry/tests/generic.c b/source4/lib/registry/tests/generic.c index 1f0c89e058..6595f86b18 100644 --- a/source4/lib/registry/tests/generic.c +++ b/source4/lib/registry/tests/generic.c @@ -3,7 +3,7 @@ local testing of registry library - Copyright (C) Jelmer Vernooij 2005 + 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 @@ -25,6 +25,10 @@ #include "torture/torture.h" #include "librpc/gen_ndr/winreg.h" +struct torture_suite *torture_registry_hive(TALLOC_CTX *mem_ctx); +struct torture_suite *torture_registry_registry(TALLOC_CTX *mem_ctx); +struct torture_suite *torture_registry_diff(TALLOC_CTX *mem_ctx); + static bool test_str_regtype(struct torture_context *ctx) { torture_assert_str_equal(ctx, str_regtype(1), "REG_SZ", "REG_SZ failed"); @@ -38,7 +42,8 @@ static bool test_reg_val_data_string_dword(struct torture_context *ctx) { uint32_t d = 0x20; DATA_BLOB db = { (uint8_t *)&d, sizeof(d) }; - torture_assert_str_equal(ctx, "0x20", reg_val_data_string(ctx, REG_DWORD, &db), "dword failed"); + torture_assert_str_equal(ctx, "0x20", + reg_val_data_string(ctx, REG_DWORD, db), "dword failed"); return true; } @@ -46,9 +51,9 @@ static bool test_reg_val_data_string_sz(struct torture_context *ctx) { DATA_BLOB db; db.length = convert_string_talloc(ctx, CH_UNIX, CH_UTF16, "bla", 3, (void **)&db.data); - torture_assert_str_equal(ctx, "bla", reg_val_data_string(ctx, REG_SZ, &db), "sz failed"); + 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"); + torture_assert_str_equal(ctx, "bl", reg_val_data_string(ctx, REG_SZ, db), "sz failed"); return true; } @@ -56,7 +61,9 @@ 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"); + torture_assert_str_equal(ctx, "01020304", + reg_val_data_string(ctx, REG_BINARY, db), + "binary failed"); return true; } @@ -64,18 +71,20 @@ static bool test_reg_val_data_string_binary(struct torture_context *ctx) 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"); + 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) { - struct registry_value val; - val.name = "camel"; - val.data_type = REG_SZ; - val.data.length = convert_string_talloc(ctx, CH_UNIX, CH_UTF16, "stationary traveller", - strlen("stationary traveller"), (void **)&val.data.data); - torture_assert_str_equal(ctx, "camel = REG_SZ : stationary traveller", reg_val_description(ctx, &val), + DATA_BLOB data; + data.length = convert_string_talloc(ctx, CH_UNIX, CH_UTF16, + "stationary traveller", + strlen("stationary traveller"), + (void **)&data.data); + torture_assert_str_equal(ctx, "camel = REG_SZ : stationary traveller", + reg_val_description(ctx, "camel", REG_SZ, data), "reg_val_description failed"); return true; } @@ -83,12 +92,11 @@ static bool test_reg_val_description(struct torture_context *ctx) static bool test_reg_val_description_nullname(struct torture_context *ctx) { - struct registry_value val; - val.name = NULL; - val.data_type = REG_SZ; - val.data.length = convert_string_talloc(ctx, CH_UNIX, CH_UTF16, "west berlin", - strlen("west berlin"), (void **)&val.data.data); - torture_assert_str_equal(ctx, "<No Name> = REG_SZ : west berlin", reg_val_description(ctx, &val), + DATA_BLOB data; + data.length = convert_string_talloc(ctx, CH_UNIX, CH_UTF16, "west berlin", + strlen("west berlin"), (void **)&data.data); + 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; } @@ -98,12 +106,22 @@ 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 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_simple_test(suite, "reg_val_data_string dword", + test_reg_val_data_string_dword); + 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(mem_ctx)); + torture_suite_add_suite(suite, torture_registry_registry(mem_ctx)); + torture_suite_add_suite(suite, torture_registry_diff(mem_ctx)); return suite; } diff --git a/source4/lib/registry/tests/hive.c b/source4/lib/registry/tests/hive.c new file mode 100644 index 0000000000..a04bc1168e --- /dev/null +++ b/source4/lib/registry/tests/hive.c @@ -0,0 +1,383 @@ +/* + 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 2 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 "lib/cmdline/popt_common.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/winreg.h" +#include "system/filesys.h" + +NTSTATUS torture_temp_dir(struct torture_context *tctx, const char *prefix, + const char **tempdir); + +static bool test_del_nonexistant_key(struct torture_context *tctx, + const void *test_data) +{ + const struct hive_key *root = test_data; + WERROR error = hive_key_del(root, "bla"); + torture_assert_werr_equal(tctx, error, WERR_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 = 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); + 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, + const void *test_data) +{ + uint32_t num_subkeys, num_values; + const struct hive_key *root = test_data; + WERROR error; + struct hive_key *subkey; + uint32_t data = 42; + + error = hive_key_add_name(tctx, root, "Nested Keyll", NULL, + NULL, &subkey); + torture_assert_werr_ok(tctx, error, "hive_key_add_name"); + + error = hive_set_value(root, "Answer", REG_DWORD, + data_blob_talloc(tctx, &data, sizeof(data))); + torture_assert_werr_ok(tctx, error, "hive_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); + 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 = 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(root, "Nested Key"); + torture_assert_werr_ok(tctx, error, "reg_key_del"); + + return true; +} + +static bool test_flush_key(struct torture_context *tctx, + const void *test_data) +{ + const struct hive_key *root = 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 = 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(root, "Nested Key"); + torture_assert_werr_ok(tctx, error, "reg_key_del"); + + error = hive_key_del(root, "Nested Key"); + torture_assert_werr_equal(tctx, error, WERR_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 = test_data; + TALLOC_CTX *mem_ctx = tctx; + uint32_t data = 42; + + 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_set_value(subkey, "Answer", REG_DWORD, + data_blob_talloc(mem_ctx, &data, sizeof(data))); + torture_assert_werr_ok(tctx, error, "hive_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 = test_data; + TALLOC_CTX *mem_ctx = tctx; + uint32_t data = 42; + uint32_t type; + DATA_BLOB value; + + 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, &value); + torture_assert_werr_equal(tctx, error, WERR_NOT_FOUND, + "getting missing value"); + + error = hive_set_value(subkey, "Answer", REG_DWORD, + data_blob_talloc(mem_ctx, &data, sizeof(data))); + torture_assert_werr_ok(tctx, error, "hive_set_value"); + + error = hive_get_value(mem_ctx, subkey, "Answer", &type, &value); + torture_assert_werr_ok(tctx, error, "getting value"); + + torture_assert(tctx, memcmp(value.data, &data, 4) == 0, "value data"); + + torture_assert_int_equal(tctx, value.length, 4, "value length"); + torture_assert_int_equal(tctx, type, REG_DWORD, "value type"); + + 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 = test_data; + TALLOC_CTX *mem_ctx = tctx; + uint32_t data = 42; + uint32_t type; + DATA_BLOB value; + + 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_set_value(subkey, "Answer", REG_DWORD, + data_blob_talloc(mem_ctx, &data, sizeof(data))); + torture_assert_werr_ok(tctx, error, "hive_set_value"); + + error = hive_del_value(subkey, "Answer"); + torture_assert_werr_ok(tctx, error, "deleting value"); + + error = hive_get_value(mem_ctx, subkey, "Answer", &type, &value); + torture_assert_werr_equal(tctx, error, WERR_NOT_FOUND, "getting value"); + + error = hive_del_value(subkey, "Answer"); + torture_assert_werr_equal(tctx, error, WERR_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 = test_data; + TALLOC_CTX *mem_ctx = tctx; + uint32_t data = 42; + uint32_t type; + DATA_BLOB value; + 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_set_value(subkey, "Answer", REG_DWORD, + data_blob_talloc(mem_ctx, &data, sizeof(data))); + torture_assert_werr_ok(tctx, error, "hive_set_value"); + + error = hive_get_value_by_index(mem_ctx, subkey, 0, &name, &type, &value); + torture_assert_werr_ok(tctx, error, "getting value"); + + torture_assert_str_equal(tctx, name, "Answer", "value name"); + torture_assert(tctx, memcmp(value.data, &data, 4) == 0, "value data"); + + torture_assert_int_equal(tctx, value.length, 4, "value length"); + torture_assert_int_equal(tctx, type, REG_DWORD, "value type"); + + error = hive_get_value_by_index(mem_ctx, subkey, 1, &name, &type, &value); + torture_assert_werr_equal(tctx, error, WERR_NO_MORE_ITEMS, + "getting missing value"); + + return true; +} + +static void tcase_add_tests(struct torture_tcase *tcase) +{ + torture_tcase_add_simple_test(tcase, "del_nonexistant_key", + test_del_nonexistant_key); + torture_tcase_add_simple_test(tcase, "add_subkey", test_add_subkey); + torture_tcase_add_simple_test(tcase, "flush_key", test_flush_key); + torture_tcase_add_simple_test(tcase, "get_info", test_keyinfo_root); + torture_tcase_add_simple_test(tcase, "get_info_nums", test_keyinfo_nums); + 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); +} + +static bool hive_setup_dir(struct torture_context *tctx, void **data) +{ + struct hive_key *key; + WERROR error; + const char *dirname; + NTSTATUS status; + + status = torture_temp_dir(tctx, "hive-dir", &dirname); + if (!NT_STATUS_IS_OK(status)) + return false; + + rmdir(dirname); + + error = reg_create_directory(tctx, dirname, &key); + if (!W_ERROR_IS_OK(error)) { + fprintf(stderr, "Unable to initialize dir hive\n"); + return false; + } + + *data = key; + + return true; +} + +static bool hive_setup_ldb(struct torture_context *tctx, void **data) +{ + struct hive_key *key; + WERROR error; + const 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, &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; + const char *dirname; + NTSTATUS status; + + status = torture_temp_dir(tctx, "hive-dir", &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; +} + +static bool test_dir_refuses_null_location(struct torture_context *tctx) +{ + torture_assert_werr_equal(tctx, WERR_INVALID_PARAM, + reg_open_directory(NULL, NULL, NULL), + "reg_open_directory accepts NULL location"); + 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"); + + torture_suite_add_simple_test(suite, "dir-refuses-null-location", + test_dir_refuses_null_location); + + + tcase = torture_suite_add_tcase(suite, "dir"); + torture_tcase_set_fixture(tcase, hive_setup_dir, NULL); + tcase_add_tests(tcase); + + 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 0000000000..851f74fa3c --- /dev/null +++ b/source4/lib/registry/tests/registry.c @@ -0,0 +1,486 @@ +/* + 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 2 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 "lib/cmdline/popt_common.h" +#include "torture/torture.h" +#include "librpc/gen_ndr/winreg.h" +#include "system/filesys.h" + +NTSTATUS torture_temp_dir(struct torture_context *tctx, const char *prefix, + const char **tempdir); + +/** + * Test obtaining a predefined key. + */ +static bool test_get_predefined(struct torture_context *tctx, + const void *_data) +{ + const struct registry_context *rctx = _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 creating a new subkey + */ +static bool test_create_subkey(struct torture_context *tctx, + const void *_data) +{ + const struct registry_context *rctx = _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, + const void *_data) +{ + const struct registry_context *rctx = _data; + struct registry_key *root, *newkey1, *newkey2; + 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", NULL, NULL, + &newkey1); + torture_assert_werr_ok(tctx, error, "Creating key return code"); + torture_assert(tctx, newkey2 != NULL, "Creating new key"); + + error = reg_key_add_name(rctx, root, "Hamburg\\Hamburg", NULL, NULL, + &newkey2); + torture_assert_werr_ok(tctx, error, "Creating key return code"); + torture_assert(tctx, newkey2 != NULL, "Creating new key"); + + return true; +} + +/** + * Test creating a new subkey + */ +static bool test_key_add_abs_top(struct torture_context *tctx, + const void *_data) +{ + const struct registry_context *rctx = _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, + const void *_data) +{ + WERROR error; + const struct registry_context *rctx = _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, const void *_data) +{ + const struct registry_context *rctx = _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", 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(root, "Hamburg"); + torture_assert_werr_ok(tctx, error, "Delete key"); + + error = reg_key_del(root, "Hamburg"); + torture_assert_werr_equal(tctx, error, WERR_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, + const 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, const void *_data) +{ + const struct registry_context *rctx = _data; + struct registry_key *root, *subkey; + WERROR error; + + if (!create_test_key(tctx, rctx, "Munchen", &root, &subkey)) + return false; + + error = reg_key_flush(subkey); + torture_assert_werr_ok(tctx, error, "flush key"); + + return true; +} + +static bool test_query_key(struct torture_context *tctx, const void *_data) +{ + const struct registry_context *rctx = _data; + struct registry_key *root, *subkey; + WERROR error; + NTTIME last_changed_time; + uint32_t num_subkeys, num_values; + const char *classname; + + if (!create_test_key(tctx, rctx, "Munchen", &root, &subkey)) + return false; + + error = reg_key_get_info(tctx, subkey, &classname, + &num_subkeys, &num_values, + &last_changed_time); + + 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"); + + return true; +} + +static bool test_query_key_nums(struct torture_context *tctx, const void *_data) +{ + const struct registry_context *rctx = _data; + struct registry_key *root, *subkey1, *subkey2; + WERROR error; + uint32_t num_subkeys, num_values; + uint32_t data = 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); + + 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, const void *_data) +{ + const struct registry_context *rctx = _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, const void *_data) +{ + const struct registry_context *rctx = _data; + struct registry_key *subkey = NULL, *root; + WERROR error; + uint32_t data = 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 a value + */ +static bool test_get_value(struct torture_context *tctx, const void *_data) +{ + const struct registry_context *rctx = _data; + struct registry_key *subkey = NULL, *root; + WERROR error; + DATA_BLOB data; + uint32_t value = 42; + uint32_t type; + + 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_NOT_FOUND, + "getting missing value"); + + error = reg_val_set(subkey, __FUNCTION__, REG_DWORD, + data_blob_talloc(tctx, &value, 4)); + 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, 4, data.length, "value length ok"); + torture_assert(tctx, memcmp(data.data, &value, 4) == 0, "value content ok"); + torture_assert_int_equal(tctx, REG_DWORD, type, "value type"); + + return true; +} + +/** + * Test unsetting a value + */ +static bool test_del_value(struct torture_context *tctx, const void *_data) +{ + const struct registry_context *rctx = _data; + struct registry_key *subkey = NULL, *root; + WERROR error; + DATA_BLOB data; + uint32_t value = 42; + uint32_t type; + + 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_NOT_FOUND, + "getting missing value"); + + error = reg_val_set(subkey, __FUNCTION__, REG_DWORD, + data_blob_talloc(tctx, &value, 4)); + torture_assert_werr_ok (tctx, error, "setting value"); + + error = reg_del_value(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_NOT_FOUND, + "getting missing value"); + + return true; +} + +/** + * Test listing values + */ +static bool test_list_values(struct torture_context *tctx, const void *_data) +{ + const struct registry_context *rctx = _data; + struct registry_key *subkey = NULL, *root; + WERROR error; + DATA_BLOB data; + uint32_t value = 42; + uint32_t type; + const char *name; + + if (!create_test_key(tctx, rctx, "Bonn", &root, &subkey)) + return false; + + error = reg_val_set(subkey, "bar", REG_DWORD, + data_blob_talloc(tctx, &value, 4)); + 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, 4, data.length, "value length"); + torture_assert(tctx, memcmp(data.data, &value, 4) == 0, "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"); + + return true; +} + +static bool setup_local_registry(struct torture_context *tctx, void **data) +{ + struct registry_context *rctx; + WERROR error; + const char *tempdir; + NTSTATUS status; + struct hive_key *hive_key; + + error = reg_open_local(tctx, &rctx, NULL, NULL); + if (!W_ERROR_IS_OK(error)) + return false; + + status = torture_temp_dir(tctx, "registry-local", &tempdir); + if (!NT_STATUS_IS_OK(status)) + return false; + + error = reg_open_ldb_file(tctx, + talloc_asprintf(tctx, "%s/classes_root.ldb", tempdir), + NULL, + NULL, + &hive_key); + if (!W_ERROR_IS_OK(error)) + return false; + + error = reg_mount_hive(rctx, hive_key, HKEY_CLASSES_ROOT, NULL); + if (!W_ERROR_IS_OK(error)) + return false; + + *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, "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); +} + +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 0000000000..c8b0945c2c --- /dev/null +++ b/source4/lib/registry/tools/common.c @@ -0,0 +1,75 @@ +/* + 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" + +struct registry_context *reg_common_open_remote(const char *remote, struct cli_credentials *creds) +{ + struct registry_context *h; + WERROR error; + + error = reg_open_remote(&h, NULL, creds, remote, NULL); + + 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 cli_credentials *creds) +{ + struct hive_key *hive_root; + struct registry_context *h; + WERROR error; + + error = reg_open_hive(NULL, path, NULL, creds, &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, NULL, creds); + 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) +{ + WERROR error; + struct registry_context *h; + + error = reg_open_samba(NULL, &h, 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 index 6eb8a78caf..8030457f5c 100644 --- a/source4/lib/registry/tools/regdiff.c +++ b/source4/lib/registry/tools/regdiff.c @@ -2,7 +2,8 @@ Unix SMB/CIFS implementation. simple registry frontend - Copyright (C) Jelmer Vernooij 2004-2005 + 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 @@ -22,63 +23,115 @@ #include "lib/registry/registry.h" #include "lib/events/events.h" #include "lib/cmdline/popt_common.h" +#include "lib/registry/tools/common.h" -int main(int argc, char **argv) +enum reg_backend { REG_UNKNOWN, REG_LOCAL, REG_REMOTE, REG_NULL }; + +static struct registry_context *open_backend(poptContext pc, enum reg_backend backend, const char *remote_host) +{ + WERROR error; + struct registry_context *ctx; + + switch (backend) { + case REG_UNKNOWN: + poptPrintUsage(pc, stderr, 0); + return NULL; + case REG_LOCAL: + error = reg_open_samba(NULL, &ctx, NULL, cmdline_credentials); + break; + case REG_REMOTE: + error = reg_open_remote(&ctx, NULL, cmdline_credentials, remote_host, NULL); + break; + case REG_NULL: + error = reg_open_local(NULL, &ctx, NULL, cmdline_credentials); + break; + } + + if (!W_ERROR_IS_OK(error)) { + fprintf(stderr, "Error: %s\n", win_errstr(error)); + return NULL; + } + + return ctx; +} + +int main(int argc, 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; - int from_null = 0; WERROR error; - struct reg_diff *diff; struct poptOption long_options[] = { POPT_AUTOHELP - {"output", 'o', POPT_ARG_STRING, &outputfile, 'o', "output file to use", NULL }, - {"null", 'n', POPT_ARG_NONE, &from_null, 'n', "Diff from NULL", NULL }, - {"remote", 'R', POPT_ARG_STRING, NULL, 0, "Connect to remote server" , NULL }, - {"local", 'L', POPT_ARG_NONE, NULL, 0, "Open local registry", NULL }, + {"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 { NULL } }; + TALLOC_CTX *ctx; + void *callback_data; + struct reg_diff_callbacks *callbacks; - registry_init(); + ctx = talloc_init("regdiff"); - pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0); + pc = poptGetContext(argv[0], argc, argv, long_options,0); while((opt = poptGetNextOpt(pc)) != -1) { error = WERR_OK; switch(opt) { case 'L': - if (!h1 && !from_null) error = reg_open_local(NULL, &h1, NULL, cmdline_credentials); - else if (!h2) error = reg_open_local(NULL, &h2, NULL, cmdline_credentials); + 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 (!h1 && !from_null) - error = reg_open_remote(&h1, NULL, cmdline_credentials, - poptGetOptArg(pc), NULL); - else if (!h2) error = reg_open_remote(&h2, NULL, cmdline_credentials, - poptGetOptArg(pc), NULL); + if (backend1 == REG_UNKNOWN) { + backend1 = REG_REMOTE; + remote1 = poptGetOptArg(pc); + } else if (backend2 == REG_UNKNOWN) { + backend2 = REG_REMOTE; + remote2 = poptGetOptArg(pc); + } break; } - if (!W_ERROR_IS_OK(error)) { - fprintf(stderr, "Error: %s\n", win_errstr(error)); - return 1; - } } + h1 = open_backend(pc, backend1, remote1); + if (h1 == NULL) + return 1; + + h2 = open_backend(pc, backend2, remote2); + if (h2 == NULL) + return 1; + poptFreeContext(pc); - diff = reg_generate_diff(NULL, h1, h2); - if (!diff) { - fprintf(stderr, "Unable to generate diff between keys\n"); + 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; } - reg_diff_save(diff, outputfile); + 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 index 83ad5575ef..1e6d15a7af 100644 --- a/source4/lib/registry/tools/regpatch.c +++ b/source4/lib/registry/tools/regpatch.c @@ -2,7 +2,7 @@ Unix SMB/CIFS implementation. simple registry frontend - Copyright (C) 2004-2005 Jelmer Vernooij, jelmer@samba.org + 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 @@ -22,6 +22,8 @@ #include "lib/events/events.h" #include "lib/registry/registry.h" #include "lib/cmdline/popt_common.h" +#include "lib/registry/tools/common.h" +#include "lib/registry/patchfile.h" int main(int argc, char **argv) { @@ -29,12 +31,12 @@ int main(int argc, char **argv) poptContext pc; const char *patch; struct registry_context *h; + const char *file = NULL; const char *remote = NULL; - struct reg_diff *diff; - WERROR error; 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 { NULL } @@ -45,29 +47,24 @@ int main(int argc, char **argv) while((opt = poptGetNextOpt(pc)) != -1) { } - registry_init(); - if (remote) { - error = reg_open_remote (&h, NULL, cmdline_credentials, remote, NULL); + h = reg_common_open_remote (remote, cmdline_credentials); } else { - error = reg_open_local (NULL, &h, NULL, cmdline_credentials); + h = reg_common_open_local (cmdline_credentials); } - - if (W_ERROR_IS_OK(error)) { - fprintf(stderr, "Error: %s\n", win_errstr(error)); + + if (h == NULL) return 1; - } patch = poptGetArg(pc); - poptFreeContext(pc); - - diff = reg_diff_load(NULL, patch); - if (!diff) { - fprintf(stderr, "Unable to load registry patch from `%s'\n", patch); + if (patch == NULL) { + poptPrintUsage(pc, stderr, 0); return 1; } - reg_diff_apply(diff, h); + poptFreeContext(pc); + + reg_diff_apply(patch, h); return 0; } diff --git a/source4/lib/registry/tools/regshell.c b/source4/lib/registry/tools/regshell.c index f431c81bf8..0887bc91f3 100644 --- a/source4/lib/registry/tools/regshell.c +++ b/source4/lib/registry/tools/regshell.c @@ -2,7 +2,7 @@ Unix SMB/CIFS implementation. simple registry frontend - Copyright (C) Jelmer Vernooij 2004 + 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 @@ -25,8 +25,15 @@ #include "system/time.h" #include "lib/smbreadline/smbreadline.h" #include "librpc/gen_ndr/ndr_security.h" +#include "lib/registry/tools/common.h" -/* +struct regshell_context { + struct registry_context *registry; + const char *path; + struct registry_key *current; +}; + +/* * * ck/cd - change key * ls - list values/keys * rmval/rm - remove value @@ -40,29 +47,40 @@ * exit */ -static struct registry_key *cmd_info(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *cur, int argc, char **argv) +static WERROR cmd_info(struct regshell_context *ctx, int argc, char **argv) { struct security_descriptor *sec_desc = NULL; time_t last_mod; WERROR error; + const char *classname; + NTTIME last_change; + + error = reg_key_get_info(ctx, ctx->current, &classname, NULL, NULL, &last_change); + if (!W_ERROR_IS_OK(error)) { + printf("Error getting key info: %s\n", win_errstr(error)); + return error; + } + - printf("Name: %s\n", cur->name); - printf("Full path: %s\n", cur->path); - printf("Key Class: %s\n", cur->class_name); - last_mod = nt_time_to_unix(cur->last_mod); + printf("Name: %s\n", strchr(ctx->path, '\\')?strrchr(ctx->path, '\\')+1: + ctx->path); + printf("Full path: %s\n", ctx->path); + printf("Key Class: %s\n", classname); + last_mod = nt_time_to_unix(last_change); printf("Time Last Modified: %s\n", ctime(&last_mod)); - error = reg_get_sec_desc(mem_ctx, cur, &sec_desc); + error = reg_get_sec_desc(ctx, ctx->current, &sec_desc); if (!W_ERROR_IS_OK(error)) { printf("Error getting security descriptor\n"); - } else { - ndr_print_debug((ndr_print_fn_t)ndr_print_security_descriptor, "Security", sec_desc); - } + return error; + } + ndr_print_debug((ndr_print_fn_t)ndr_print_security_descriptor, "Security", sec_desc); talloc_free(sec_desc); - return cur; + + return WERR_OK; } -static struct registry_key *cmd_predef(TALLOC_CTX *mem_ctx, struct registry_context *ctx, struct registry_key *cur, int argc, char **argv) +static WERROR cmd_predef(struct regshell_context *ctx, int argc, char **argv) { struct registry_key *ret = NULL; if (argc < 2) { @@ -70,165 +88,195 @@ static struct registry_key *cmd_predef(TALLOC_CTX *mem_ctx, struct registry_cont } else if (!ctx) { fprintf(stderr, "No full registry loaded, no predefined keys defined\n"); } else { - WERROR error = reg_get_predefined_key_by_name(ctx, argv[1], &ret); + 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)); - ret = NULL; + return error; } } - return ret; + + return WERR_OK; } -static struct registry_key *cmd_pwd(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *cur, int argc, char **argv) +static WERROR cmd_pwd(struct regshell_context *ctx, + int argc, char **argv) { - printf("%s\n", cur->path); - return cur; + printf("%s\n", ctx->path); + return WERR_OK; } -static struct registry_key *cmd_set(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *cur, int argc, char **argv) +static WERROR cmd_set(struct regshell_context *ctx, int argc, char **argv) { struct registry_value val; WERROR error; if (argc < 4) { fprintf(stderr, "Usage: set value-name type value\n"); - return cur; + return WERR_INVALID_PARAM; } - if (!reg_string_to_val(mem_ctx, argv[2], argv[3], &val.data_type, &val.data)) { + if (!reg_string_to_val(ctx, argv[2], argv[3], &val.data_type, + &val.data)) { fprintf(stderr, "Unable to interpret data\n"); - return cur; + return WERR_INVALID_PARAM; } - error = reg_val_set(cur, argv[1], val.data_type, val.data); + 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 NULL; + return error; } - return cur; + + return WERR_OK; } -static struct registry_key *cmd_ck(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *cur, int argc, char **argv) +static WERROR cmd_ck(struct regshell_context *ctx, int argc, char **argv) { struct registry_key *new = NULL; WERROR error; + if(argc < 2) { - new = cur; + new = ctx->current; } else { - error = reg_open_key(mem_ctx, cur, argv[1], &new); + error = reg_open_key(ctx->registry, ctx->current, argv[1], &new); if(!W_ERROR_IS_OK(error)) { DEBUG(0, ("Error opening specified key: %s\n", win_errstr(error))); - return NULL; + return error; } } - printf("Current path is: %s\n", new->path); + ctx->path = talloc_asprintf(ctx, "%s\\%s", ctx->path, argv[1]); + printf("Current path is: %s\n", ctx->path); + ctx->current = new; - return new; + return WERR_OK; } -static struct registry_key *cmd_print(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *cur, int argc, char **argv) +static WERROR cmd_print(struct regshell_context *ctx, int argc, char **argv) { - struct registry_value *value; + uint32_t value_type; + DATA_BLOB value_data; WERROR error; if (argc != 2) { fprintf(stderr, "Usage: print <valuename>"); - return NULL; + return WERR_INVALID_PARAM; } - error = reg_key_get_value_by_name(mem_ctx, cur, argv[1], &value); + 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 NULL; + return error; } - printf("%s\n%s\n", str_regtype(value->data_type), reg_val_data_string(mem_ctx, value->data_type, &value->data)); - return NULL; + printf("%s\n%s\n", str_regtype(value_type), + reg_val_data_string(ctx, value_type, value_data)); + + return WERR_OK; } -static struct registry_key *cmd_ls(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *cur, int argc, char **argv) +static WERROR cmd_ls(struct regshell_context *ctx, int argc, char **argv) { int i; WERROR error; struct registry_value *value; - struct registry_key *sub; - for(i = 0; W_ERROR_IS_OK(error = reg_key_get_subkey_by_index(mem_ctx, cur, i, &sub)); i++) { - printf("K %s\n", sub->name); + uint32_t data_type; + DATA_BLOB data; + const char *name; + + 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)) { + if (!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) { DEBUG(0, ("Error occured while browsing thru keys: %s\n", win_errstr(error))); } - for(i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(mem_ctx, cur, i, &value)); i++) { - printf("V \"%s\" %s %s\n", value->name, str_regtype(value->data_type), reg_val_data_string(mem_ctx, value->data_type, &value->data)); + for (i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(ctx, ctx->current, i, &name, &data_type, &data)); i++) { + printf("V \"%s\" %s %s\n", value->name, str_regtype(data_type), + reg_val_data_string(ctx, data_type, data)); } - return NULL; + return WERR_OK; } -static struct registry_key *cmd_mkkey(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *cur, int argc, char **argv) +static WERROR cmd_mkkey(struct regshell_context *ctx, int argc, char **argv) { struct registry_key *tmp; + WERROR error; + if(argc < 2) { fprintf(stderr, "Usage: mkkey <keyname>\n"); - return NULL; + return WERR_INVALID_PARAM; } + + error = reg_key_add_name(ctx, ctx->current, argv[1], 0, NULL, &tmp); - if(!W_ERROR_IS_OK(reg_key_add_name(mem_ctx, cur, argv[1], 0, NULL, &tmp))) { + if (!W_ERROR_IS_OK(error)) { fprintf(stderr, "Error adding new subkey '%s'\n", argv[1]); - return NULL; + return error; } - return NULL; + return WERR_OK; } -static struct registry_key *cmd_rmkey(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *cur, int argc, char **argv) +static WERROR cmd_rmkey(struct regshell_context *ctx, + int argc, char **argv) { + WERROR error; + if(argc < 2) { fprintf(stderr, "Usage: rmkey <name>\n"); - return NULL; + return WERR_INVALID_PARAM; } - if(!W_ERROR_IS_OK(reg_key_del(cur, argv[1]))) { + error = reg_key_del(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 NULL; + return WERR_OK; } -static struct registry_key *cmd_rmval(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *cur, int argc, char **argv) +static WERROR cmd_rmval(struct regshell_context *ctx, int argc, char **argv) { + WERROR error; + if(argc < 2) { fprintf(stderr, "Usage: rmval <valuename>\n"); - return NULL; + return WERR_INVALID_PARAM; } - if(!W_ERROR_IS_OK(reg_del_value(cur, argv[1]))) { + error = reg_del_value(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 NULL; + return WERR_OK; } -static struct registry_key *cmd_exit(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *cur, int argc, char **argv) +static WERROR cmd_exit(struct regshell_context *ctx, + int argc, char **argv) { exit(0); - return NULL; + return WERR_OK; } -static struct registry_key *cmd_help(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *, int, char **); +static WERROR cmd_help(struct regshell_context *ctx, int, char **); static struct { const char *name; const char *alias; const char *help; - struct registry_key *(*handle)(TALLOC_CTX *mem_ctx, struct registry_context *ctx,struct registry_key *, int argc, char **argv); + WERROR (*handle)(struct regshell_context *ctx, + int argc, char **argv); } regshell_cmds[] = { {"ck", "cd", "Change current key", cmd_ck }, {"info", "i", "Show detailed information of a key", cmd_info }, @@ -245,17 +293,19 @@ static struct { {NULL } }; -static struct registry_key *cmd_help(TALLOC_CTX *mem_ctx, struct registry_context *ctx, struct registry_key *cur, int argc, char **argv) +static WERROR cmd_help(struct regshell_context *ctx, + int argc, char **argv) { 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 NULL; + return WERR_OK; } -static struct registry_key *process_cmd(TALLOC_CTX *mem_ctx, struct registry_context *ctx, struct registry_key *k, char *line) +static WERROR process_cmd(struct regshell_context *ctx, + char *line) { int argc; char **argv = NULL; @@ -263,19 +313,19 @@ static struct registry_key *process_cmd(TALLOC_CTX *mem_ctx, struct registry_con if ((ret = poptParseArgvString(line, &argc, (const char ***) &argv)) != 0) { fprintf(stderr, "regshell: %s\n", poptStrerror(ret)); - return k; + return WERR_INVALID_PARAM; } 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(mem_ctx, ctx, k, argc, argv); + return regshell_cmds[i].handle(ctx, argc, argv); } } fprintf(stderr, "No such command '%s'\n", argv[0]); - return k; + return WERR_INVALID_PARAM; } #define MAX_COMPLETIONS 100 @@ -333,7 +383,7 @@ cleanup: static char **reg_complete_key(const char *text, int start, int end) { struct registry_key *base; - struct registry_key *subkey; + const char *subkeyname; int i, j = 1; int samelen = 0; int len; @@ -351,10 +401,11 @@ static char **reg_complete_key(const char *text, int start, int end) len = strlen(text); for(i = 0; j < MAX_COMPLETIONS-1; i++) { - status = reg_key_get_subkey_by_index(mem_ctx, base, i, &subkey); + status = reg_key_get_subkey_by_index(mem_ctx, base, i, &subkeyname, + NULL, NULL); if(W_ERROR_IS_OK(status)) { - if(!strncmp(text, subkey->name, len)) { - matches[j] = strdup(subkey->name); + if(!strncmp(text, subkeyname, len)) { + matches[j] = strdup(subkeyname); j++; if (j == 1) @@ -381,7 +432,8 @@ static char **reg_complete_key(const char *text, int start, int end) if (j == 2) { /* Exact match */ asprintf(&matches[0], "%s%s", base_n, matches[1]); } else { - asprintf(&matches[0], "%s%s", base_n, talloc_strndup(mem_ctx, matches[1], samelen)); + asprintf(&matches[0], "%s%s", base_n, + talloc_strndup(mem_ctx, matches[1], samelen)); } talloc_free(mem_ctx); @@ -400,19 +452,17 @@ static char **reg_completion(const char *text, int start, int end) } } - int main(int argc, char **argv) +int main(int argc, char **argv) { int opt; - const char *backend = NULL; - struct registry_key *curkey = NULL; + const char *file = NULL; poptContext pc; - WERROR error; - TALLOC_CTX *mem_ctx = talloc_init("cmd"); const char *remote = NULL; - struct registry_context *h = NULL; + struct regshell_context *ctx; + bool ret = true; struct poptOption long_options[] = { POPT_AUTOHELP - {"backend", 'b', POPT_ARG_STRING, &backend, 0, "backend to use", NULL}, + {"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 @@ -425,64 +475,62 @@ static char **reg_completion(const char *text, int start, int end) while((opt = poptGetNextOpt(pc)) != -1) { } - registry_init(); + ctx = talloc_zero(NULL, struct regshell_context); - if (remote) { - error = reg_open_remote (&h, NULL, cmdline_credentials, remote, NULL); - } else if (backend) { - error = reg_open_hive(NULL, backend, poptGetArg(pc), NULL, cmdline_credentials, &curkey); + if (remote != NULL) { + ctx->registry = reg_common_open_remote(remote, cmdline_credentials); + } else if (file != NULL) { + ctx->current = reg_common_open_file(file, cmdline_credentials); + ctx->registry = ctx->current->context; + ctx->path = talloc_strdup(ctx, ""); } else { - error = reg_open_local(NULL, &h, NULL, cmdline_credentials); + ctx->registry = reg_common_open_local(cmdline_credentials); } - if(!W_ERROR_IS_OK(error)) { - fprintf(stderr, "Unable to open registry\n"); + if (ctx->registry == NULL) return 1; - } - if (h) { + if (ctx->current == NULL) { int i; for (i = 0; reg_predefined_keys[i].handle; i++) { WERROR err; - err = reg_get_predefined_key(h, reg_predefined_keys[i].handle, &curkey); + err = reg_get_predefined_key(ctx->registry, + reg_predefined_keys[i].handle, + &ctx->current); if (W_ERROR_IS_OK(err)) { + ctx->path = talloc_strdup(ctx, reg_predefined_keys[i].name); break; } else { - curkey = NULL; + ctx->current = NULL; } } } - if (!curkey) { + if (ctx->current == NULL) { fprintf(stderr, "Unable to access any of the predefined keys\n"); return -1; } poptFreeContext(pc); - while(True) { + while (true) { char *line, *prompt; - if(curkey->hive->root->name) { - asprintf(&prompt, "%s:%s> ", curkey->hive->root->name, curkey->path); - } else { - asprintf(&prompt, "%s> ", curkey->path); - } + asprintf(&prompt, "%s> ", ctx->path); - current_key = curkey; /* No way to pass a void * pointer - via readline :-( */ + current_key = ctx->current; /* No way to pass a void * pointer + via readline :-( */ line = smb_readline(prompt, NULL, reg_completion); - if(!line) + if (line == NULL) break; - if(line[0] != '\n') { - struct registry_key *new = process_cmd(mem_ctx, h, curkey, line); - if(new)curkey = new; + if (line[0] != '\n') { + ret = W_ERROR_IS_OK(process_cmd(ctx, line)); } } - talloc_free(mem_ctx); + talloc_free(ctx); - return 0; + return (ret?0:1); } diff --git a/source4/lib/registry/tools/regtree.c b/source4/lib/registry/tools/regtree.c index d026d2824f..8d2460a93e 100644 --- a/source4/lib/registry/tools/regtree.c +++ b/source4/lib/registry/tools/regtree.c @@ -2,7 +2,7 @@ Unix SMB/CIFS implementation. simple registry frontend - Copyright (C) Jelmer Vernooij 2004 + 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 @@ -20,52 +20,66 @@ #include "includes.h" #include "lib/registry/registry.h" +#include "lib/registry/tools/common.h" #include "lib/events/events.h" #include "lib/cmdline/popt_common.h" -static void print_tree(int l, struct registry_key *p, int fullpath, int novals) +/** + * 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(int level, struct registry_key *p, + const char *name, + bool fullpath, bool novals) { struct registry_key *subkey; - struct registry_value *value; + const char *valuename; + const char *keyname; + uint32_t value_type; + DATA_BLOB value_data; struct security_descriptor *sec_desc; WERROR error; int i; TALLOC_CTX *mem_ctx; - for(i = 0; i < l; i++) putchar(' '); - - /* Hive name */ - if(p->hive->root == p) { - if(p->hive->root->name) printf("%s\n", p->hive->root->name); else printf("<No Name>\n"); - } else { - if(!p->name) printf("<No Name>\n"); - if(fullpath) printf("%s\n", p->path); - else printf("%s\n", p->name?p->name:"(NULL)"); - } + 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, &subkey)); i++) { - print_tree(l+1, subkey, fullpath, novals); + 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(mem_ctx); if(!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) { - DEBUG(0, ("Error occured while fetching subkeys for '%s': %s\n", p->path, win_errstr(error))); + DEBUG(0, ("Error occured while fetching subkeys for '%s': %s\n", + name, win_errstr(error))); } - if(!novals) { + 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, &value)); i++) { + for(i = 0; W_ERROR_IS_OK(error = reg_key_get_value_by_index(mem_ctx, + p, i, &valuename, &value_type, &value_data)); i++) { int j; char *desc; - for(j = 0; j < l+1; j++) putchar(' '); - desc = reg_val_description(mem_ctx, value); + for(j = 0; j < level+1; j++) putchar(' '); + desc = reg_val_description(mem_ctx, valuename, value_type, + value_data); printf("%s\n", desc); } talloc_free(mem_ctx); if(!W_ERROR_EQUAL(error, WERR_NO_MORE_ITEMS)) { - DEBUG(0, ("Error occured while fetching values for '%s': %s\n", p->path, win_errstr(error))); + DEBUG(0, ("Error occured while fetching values for '%s': %s\n", + name, win_errstr(error))); } } @@ -79,21 +93,22 @@ static void print_tree(int l, struct registry_key *p, int fullpath, int novals) int main(int argc, char **argv) { int opt, i; - const char *backend = NULL; + const char *file = NULL; const char *remote = NULL; poptContext pc; struct registry_context *h = NULL; - struct registry_key *root = NULL; + struct registry_key *start_key = NULL; WERROR error; - int fullpath = 0, no_values = 0; + bool fullpath = false, no_values = false; struct poptOption long_options[] = { POPT_AUTOHELP - {"backend", 'b', POPT_ARG_STRING, &backend, 0, "backend to use", NULL}, - {"fullpath", 'f', POPT_ARG_NONE, &fullpath, 0, "show full paths", NULL}, + {"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 { NULL } }; @@ -102,48 +117,35 @@ int main(int argc, char **argv) while((opt = poptGetNextOpt(pc)) != -1) { } - registry_init(); - - if (remote) { - error = reg_open_remote(&h, NULL, cmdline_credentials, remote, NULL); - - if(!W_ERROR_IS_OK(error)) { - fprintf(stderr, "Unable to open remote registry at %s:%s \n", remote, win_errstr(error)); - return 1; - } - - } else if (backend) { - error = reg_open_hive(NULL, backend, poptGetArg(pc), NULL, cmdline_credentials, &root); - - if(!W_ERROR_IS_OK(error)) { - fprintf(stderr, "Unable to open '%s' with backend '%s':%s \n", poptGetArg(pc), backend, win_errstr(error)); - return 1; - } + if (remote != NULL) { + h = reg_common_open_remote(remote, cmdline_credentials); + } else if (file != NULL) { + start_key = reg_common_open_file(file, cmdline_credentials); } else { - error = reg_open_local (NULL, &h, NULL, cmdline_credentials); - - if(!W_ERROR_IS_OK(error)) { - fprintf(stderr, "Unable to open local registry:%s \n", win_errstr(error)); - return 1; - } - + h = reg_common_open_local(cmdline_credentials); } + if (h == NULL && start_key == NULL) + return 1; + poptFreeContext(pc); error = WERR_OK; - if (root != NULL) { - print_tree(0, root, fullpath, no_values); + 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, &root); + error = reg_get_predefined_key(h, reg_predefined_keys[i].handle, + &start_key); if (!W_ERROR_IS_OK(error)) { - fprintf(stderr, "Skipping %s\n", reg_predefined_keys[i].name); + fprintf(stderr, "Skipping %s: %s\n", reg_predefined_keys[i].name, + win_errstr(error)); continue; } - SMB_ASSERT(root); - print_tree(0, root, fullpath, no_values); + SMB_ASSERT(start_key != NULL); + print_tree(0, start_key, reg_predefined_keys[i].name, fullpath, + no_values); } } diff --git a/source4/lib/registry/common/reg_util.c b/source4/lib/registry/util.c index 696336161e..47716f89cf 100644 --- a/source4/lib/registry/common/reg_util.c +++ b/source4/lib/registry/util.c @@ -1,7 +1,7 @@ /* Unix SMB/CIFS implementation. Transparent registry backend handling - Copyright (C) Jelmer Vernooij 2003-2004. + 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 @@ -50,27 +50,30 @@ _PUBLIC_ const char *str_regtype(int type) return "Unknown"; } -_PUBLIC_ char *reg_val_data_string(TALLOC_CTX *mem_ctx, uint32_t type, DATA_BLOB *data) +_PUBLIC_ char *reg_val_data_string(TALLOC_CTX *mem_ctx, uint32_t type, + const DATA_BLOB data) { char *ret = NULL; - if(data->length == 0) return talloc_strdup(mem_ctx, ""); + 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); + convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX, data.data, data.length, + (void **)&ret); return ret; case REG_BINARY: - ret = data_blob_hex_string(mem_ctx, data); + ret = data_blob_hex_string(mem_ctx, &data); return ret; case REG_DWORD: - if (*(int *)data->data == 0) + if (*(int *)data.data == 0) return talloc_strdup(mem_ctx, "0"); - return talloc_asprintf(mem_ctx, "0x%x", *(int *)data->data); + return talloc_asprintf(mem_ctx, "0x%x", *(int *)data.data); case REG_MULTI_SZ: /* FIXME */ @@ -84,9 +87,13 @@ _PUBLIC_ char *reg_val_data_string(TALLOC_CTX *mem_ctx, uint32_t type, DATA_BLOB } /** Generate a string that describes a registry value */ -_PUBLIC_ char *reg_val_description(TALLOC_CTX *mem_ctx, struct registry_value *val) +_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", val->name?val->name:"<No Name>", str_regtype(val->data_type), reg_val_data_string(mem_ctx, val->data_type, &val->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)); } _PUBLIC_ BOOL reg_string_to_val(TALLOC_CTX *mem_ctx, const char *type_str, const char *data_str, uint32_t *type, DATA_BLOB *data) @@ -136,29 +143,6 @@ _PUBLIC_ BOOL reg_string_to_val(TALLOC_CTX *mem_ctx, const char *type_str, const return True; } -/** - * Replace all \'s with /'s - */ -char *reg_path_win2unix(char *path) -{ - int i; - for(i = 0; path[i]; i++) { - if(path[i] == '\\') path[i] = '/'; - } - return path; -} -/** - * Replace all /'s with \'s - */ -char *reg_path_unix2win(char *path) -{ - int i; - for(i = 0; path[i]; i++) { - if(path[i] == '/') path[i] = '\\'; - } - return path; -} - /** 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) { @@ -167,14 +151,16 @@ WERROR reg_open_key_abs(TALLOC_CTX *mem_ctx, struct registry_context *handle, co int predeflength; char *predefname; - if(strchr(name, '\\')) predeflength = strchr(name, '\\')-name; - else predeflength = strlen(name); + if (strchr(name, '\\') != NULL) + predeflength = strchr(name, '\\')-name; + else + predeflength = strlen(name); predefname = talloc_strndup(mem_ctx, name, predeflength); error = reg_get_predefined_key_by_name(handle, predefname, &predef); talloc_free(predefname); - if(!W_ERROR_IS_OK(error)) { + if (!W_ERROR_IS_OK(error)) { return error; } @@ -186,7 +172,9 @@ WERROR reg_open_key_abs(TALLOC_CTX *mem_ctx, struct registry_context *handle, co } } -static WERROR get_abs_parent(TALLOC_CTX *mem_ctx, struct registry_context *ctx, const char *path, struct registry_key **parent, const char **name) +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; @@ -195,14 +183,14 @@ static WERROR get_abs_parent(TALLOC_CTX *mem_ctx, struct registry_context *ctx, return WERR_FOOBAR; } - parent_name = talloc_strndup(mem_ctx, path, strrchr(path, '\\')-1-path); + parent_name = talloc_strndup(mem_ctx, path, strrchr(path, '\\')-path); error = reg_open_key_abs(mem_ctx, ctx, parent_name, parent); if (!W_ERROR_IS_OK(error)) { return error; } - *name = talloc_strdup(mem_ctx, strchr(path, '\\')+1); + *name = talloc_strdup(mem_ctx, strrchr(path, '\\')+1); return WERR_OK; } @@ -228,20 +216,27 @@ WERROR reg_key_del_abs(struct registry_context *ctx, const char *path) 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) +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; if (!strchr(path, '\\')) { - return WERR_FOOBAR; + return WERR_ALREADY_EXISTS; } error = get_abs_parent(mem_ctx, ctx, path, &parent, &n); - if (W_ERROR_IS_OK(error)) { - error = reg_key_add_name(mem_ctx, parent, n, access_mask, sec_desc, result); + 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/reg_backend_wine.c b/source4/lib/registry/wine.c index 7184a602d1..2cb0b9955e 100644 --- a/source4/lib/registry/reg_backend_wine.c +++ b/source4/lib/registry/wine.c @@ -1,7 +1,7 @@ /* Unix SMB/CIFS implementation. Registry interface - Copyright (C) Jelmer Vernooij 2004. + 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 @@ -26,8 +26,6 @@ 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, diff --git a/source4/lib/util/become_daemon.c b/source4/lib/util/become_daemon.c index 2a01cea286..034114eade 100644 --- a/source4/lib/util/become_daemon.c +++ b/source4/lib/util/become_daemon.c @@ -28,7 +28,7 @@ /******************************************************************* Close the low 3 fd's and open dev/null in their place. ********************************************************************/ -static void close_low_fds(BOOL stderr_too) +static void close_low_fds(bool stderr_too) { #ifndef VALGRIND int fd; @@ -65,7 +65,7 @@ static void close_low_fds(BOOL stderr_too) Become a daemon, discarding the controlling terminal. **/ -_PUBLIC_ void become_daemon(BOOL Fork) +_PUBLIC_ void become_daemon(bool Fork) { if (Fork) { if (fork()) { @@ -87,7 +87,7 @@ _PUBLIC_ void become_daemon(BOOL Fork) #endif /* HAVE_SETSID */ /* Close fd's 0,1,2. Needed if started by rsh */ - close_low_fds(False); /* Don't close stderr, let the debug system + close_low_fds(false); /* Don't close stderr, let the debug system attach it to the logfile */ } |