summaryrefslogtreecommitdiff
path: root/source4/lib/registry
diff options
context:
space:
mode:
authorJelmer Vernooij <jelmer@samba.org>2004-04-04 16:24:08 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:50:33 -0500
commitc424c2b857fe08587eb81a5c5e3625545119d1c2 (patch)
tree03505d09ccd72cdfd1218066d355e2d01d403bd0 /source4/lib/registry
parent3855ee0164d1c8ff3c3c4ba8a5556d8cfb6546b3 (diff)
downloadsamba-c424c2b857fe08587eb81a5c5e3625545119d1c2.tar.gz
samba-c424c2b857fe08587eb81a5c5e3625545119d1c2.tar.bz2
samba-c424c2b857fe08587eb81a5c5e3625545119d1c2.zip
r20: Add the registry library. Still needs a lot of work,
see source/lib/registry/TODO for details. (This used to be commit 7cab3a00d7b4b1d95a3bfa6b28f318b4aaa5d493)
Diffstat (limited to 'source4/lib/registry')
-rw-r--r--source4/lib/registry/TODO27
-rw-r--r--source4/lib/registry/common/reg_display.c60
-rw-r--r--source4/lib/registry/common/reg_interface.c423
-rw-r--r--source4/lib/registry/common/reg_objects.c202
-rw-r--r--source4/lib/registry/common/reg_util.c164
-rw-r--r--source4/lib/registry/common/registry.h121
-rw-r--r--source4/lib/registry/config.m416
-rw-r--r--source4/lib/registry/reg_backend_dir/reg_backend_dir.c154
-rw-r--r--source4/lib/registry/reg_backend_gconf/reg_backend_gconf.c189
-rw-r--r--source4/lib/registry/reg_backend_ldb/reg_backend_ldb.c61
-rw-r--r--source4/lib/registry/reg_backend_nt4/reg_backend_nt4.c1745
-rw-r--r--source4/lib/registry/reg_backend_rpc/reg_backend_rpc.c311
-rw-r--r--source4/lib/registry/reg_backend_wine/reg_backend_wine.c32
-rw-r--r--source4/lib/registry/tools/regdiff.c148
-rw-r--r--source4/lib/registry/tools/regpatch.c808
-rw-r--r--source4/lib/registry/tools/regshell.c243
-rw-r--r--source4/lib/registry/tools/regtree.c91
-rw-r--r--source4/lib/registry/winregistry.pc.in10
18 files changed, 4805 insertions, 0 deletions
diff --git a/source4/lib/registry/TODO b/source4/lib/registry/TODO
new file mode 100644
index 0000000000..26fb8d217d
--- /dev/null
+++ b/source4/lib/registry/TODO
@@ -0,0 +1,27 @@
+- support subtrees
+- ../../, /bla/blie support in regshell
+- use memory pools?
+- get rid of all the nasty memory leaks..
+- security stuff
+- finish 'regpatch'
+- clean up code
+- rpc_server
+
+reg_backend_dir:
+ - value support
+
+reg_backend_nt4:
+ - write support
+
+reg_backend_rpc:
+ - value enum support
+ - write support
+
+reg_backend_ldb:
+ - implement
+
+reg_backend_wine.c:
+ - implement
+
+regpatch.c:
+ - test/finish
diff --git a/source4/lib/registry/common/reg_display.c b/source4/lib/registry/common/reg_display.c
new file mode 100644
index 0000000000..e12f4ba20e
--- /dev/null
+++ b/source4/lib/registry/common/reg_display.c
@@ -0,0 +1,60 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Gerald Carter 2001
+ Copyright (C) Tim Potter 2000
+ 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 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"
+
+void display_reg_value(REG_VAL *value)
+{
+ pstring text;
+
+ switch(reg_val_type(value)) {
+ case REG_DWORD:
+ printf("%s: REG_DWORD: 0x%08x\n", reg_val_name(value),
+ *((uint32 *) reg_val_data_blk(value)));
+ break;
+ case REG_SZ:
+ rpcstr_pull(text, reg_val_data_blk(value), sizeof(text), reg_val_size(value),
+ STR_TERMINATE);
+ printf("%s: REG_SZ: %s\n", reg_val_name(value), text);
+ break;
+ case REG_BINARY:
+ printf("%s: REG_BINARY: unknown length value not displayed\n",
+ reg_val_name(value));
+ break;
+ case REG_MULTI_SZ: {
+ uint16 *curstr = (uint16 *) reg_val_data_blk(value);
+ uint8 *start = reg_val_data_blk(value);
+ printf("%s: REG_MULTI_SZ:\n", reg_val_name(value));
+ while ((*curstr != 0) &&
+ ((uint8 *) curstr < start + reg_val_size(value))) {
+ rpcstr_pull(text, curstr, sizeof(text), -1,
+ STR_TERMINATE);
+ printf(" %s\n", text);
+ curstr += strlen(text) + 1;
+ }
+ }
+ break;
+ default:
+ printf("%s: unknown type %d\n", reg_val_name(value), reg_val_type(value));
+ }
+
+}
diff --git a/source4/lib/registry/common/reg_interface.c b/source4/lib/registry/common/reg_interface.c
new file mode 100644
index 0000000000..dc49dab4c1
--- /dev/null
+++ b/source4/lib/registry/common/reg_interface.c
@@ -0,0 +1,423 @@
+/*
+ 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 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/common/registry.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+/* 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 new backend */
+NTSTATUS registry_register(void *_function)
+{
+ REG_OPS *functions = _function;
+ struct reg_init_function_entry *entry = backends;
+
+ if (!functions || !functions->name) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ DEBUG(5,("Attempting to register registry backend %s\n", functions->name));
+
+ /* Check for duplicates */
+ if (reg_find_backend_entry(functions->name)) {
+ DEBUG(0,("There already is a registry backend registered with the name %s!\n", functions->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ entry = malloc(sizeof(struct reg_init_function_entry));
+ entry->functions = functions;
+
+ DLIST_ADD(backends, entry);
+ DEBUG(5,("Successfully added registry backend '%s'\n", functions->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 = backends;
+
+ while(entry) {
+ if (strcmp(entry->functions->name, name)==0) return entry;
+ entry = entry->next;
+ }
+
+ return NULL;
+}
+
+/* Open a registry file/host/etc */
+REG_HANDLE *reg_open(const char *backend, const char *location, BOOL try_full_load)
+{
+ struct reg_init_function_entry *entry;
+ static BOOL reg_first_init = True;
+ REG_HANDLE *ret;
+
+ if(reg_first_init) {
+ if (!NT_STATUS_IS_OK(register_subsystem("registry", registry_register))) {
+ return False;
+ }
+
+ static_init_reg;
+ reg_first_init = False;
+ }
+
+ entry = reg_find_backend_entry(backend);
+
+ if (!entry) {
+ DEBUG(0, ("No such registry backend '%s' loaded!\n", backend));
+ return NULL;
+ }
+
+ ret = malloc(sizeof(REG_HANDLE));
+ ZERO_STRUCTP(ret);
+ ret->location = location?strdup(location):NULL;
+ ret->functions = entry->functions;
+ ret->backend_data = NULL;
+
+ if(!entry->functions->open_registry) {
+ return ret;
+ }
+
+ if(entry->functions->open_registry(ret, location, try_full_load))
+ return ret;
+
+ SAFE_FREE(ret);
+ return NULL;
+}
+
+/* Open a key */
+REG_KEY *reg_open_key(REG_KEY *parent, const char *name)
+{
+ char *fullname;
+ REG_KEY *ret = NULL;
+
+ if(!parent) {
+ DEBUG(0, ("Invalid parent key specified"));
+ return NULL;
+ }
+
+ if(!parent->handle->functions->open_key &&
+ (parent->handle->functions->get_subkey_by_name ||
+ parent->handle->functions->get_subkey_by_index)) {
+ char *orig = strdup(name),
+ *curbegin = orig,
+ *curend = strchr(orig, '\\');
+ REG_KEY *curkey = parent;
+
+ while(curbegin && *curbegin) {
+ if(curend)*curend = '\0';
+ curkey = reg_key_get_subkey_by_name(curkey, curbegin);
+ if(!curkey) return NULL;
+ if(!curend) break;
+ curbegin = curend + 1;
+ curend = strchr(curbegin, '\\');
+ }
+
+ return curkey;
+ }
+
+ asprintf(&fullname, "%s%s%s", parent->path, parent->path[strlen(parent->path)-1] == '\\'?"":"\\", name);
+
+ if(!parent->handle->functions->open_key) {
+ DEBUG(0, ("Registry backend doesn't have get_subkey_by_name nor open_key!\n"));
+ return NULL;
+ }
+
+ ret = parent->handle->functions->open_key(parent->handle, fullname);
+
+ if(ret) {
+ ret->handle = parent->handle;
+ ret->path = fullname;
+ } else
+ SAFE_FREE(fullname);
+
+ return ret;
+}
+
+REG_VAL *reg_key_get_value_by_index(REG_KEY *key, int idx)
+{
+ REG_VAL *ret;
+
+ if(!key) return NULL;
+
+ if(!key->handle->functions->get_value_by_index) {
+ if(!key->cache_values)
+ key->handle->functions->fetch_values(key, &key->cache_values_count, &key->cache_values);
+
+ if(idx < key->cache_values_count && idx >= 0) {
+ ret = reg_val_dup(key->cache_values[idx]);
+ } else {
+ return NULL;
+ }
+ } else {
+ ret = key->handle->functions->get_value_by_index(key, idx);
+ }
+
+ if(ret) {
+ ret->parent = key;
+ ret->handle = key->handle;
+ }
+
+ return ret;
+}
+
+int reg_key_num_subkeys(REG_KEY *key)
+{
+ if(!key->handle->functions->num_subkeys) {
+ if(!key->cache_subkeys)
+ key->handle->functions->fetch_subkeys(key, &key->cache_subkeys_count, &key->cache_subkeys);
+
+ return key->cache_subkeys_count;
+ }
+
+ return key->handle->functions->num_subkeys(key);
+}
+
+int reg_key_num_values(REG_KEY *key)
+{
+
+ if(!key) return 0;
+
+ if(!key->handle->functions->num_values) {
+ if(!key->handle->functions->fetch_values) {
+ DEBUG(1, ("Backend '%s' doesn't support enumerating values\n", key->handle->functions->name));
+ return 0;
+ }
+
+ if(!key->cache_values)
+ key->handle->functions->fetch_values(key, &key->cache_values_count, &key->cache_values);
+
+ return key->cache_values_count;
+ }
+
+
+ return key->handle->functions->num_values(key);
+}
+
+REG_KEY *reg_key_get_subkey_by_index(REG_KEY *key, int idx)
+{
+ REG_KEY *ret = NULL;
+
+ if(!key) return NULL;
+
+ if(!key->handle->functions->get_subkey_by_index) {
+ if(!key->cache_subkeys)
+ key->handle->functions->fetch_subkeys(key, &key->cache_subkeys_count, &key->cache_subkeys);
+
+ if(idx < key->cache_subkeys_count) {
+ ret = reg_key_dup(key->cache_subkeys[idx]);
+ } else {
+ /* No such key ! */
+ return NULL;
+ }
+ } else {
+ ret = key->handle->functions->get_subkey_by_index(key, idx);
+ }
+
+ if(ret && !ret->path) {
+ asprintf(&ret->path, "%s%s%s", key->path, key->path[strlen(key->path)-1] == '\\'?"":"\\", ret->name);
+ ret->handle = key->handle;
+ }
+
+ return ret;
+}
+
+REG_KEY *reg_key_get_subkey_by_name(REG_KEY *key, const char *name)
+{
+ int i, max;
+ REG_KEY *ret = NULL;
+
+ if(!key) return NULL;
+
+ if(key->handle->functions->get_subkey_by_name) {
+ ret = key->handle->functions->get_subkey_by_name(key,name);
+ } else {
+ max = reg_key_num_subkeys(key);
+ for(i = 0; i < max; i++) {
+ REG_KEY *v = reg_key_get_subkey_by_index(key, i);
+ if(v && !strcmp(v->name, name)) {
+ ret = v;
+ break;
+ }
+ reg_key_free(v);
+ }
+ }
+
+ if(ret && !ret->path) {
+ asprintf(&ret->path, "%s%s%s", key->path, key->path[strlen(key->path)-1] == '\\'?"":"\\", ret->name);
+ ret->handle = key->handle;
+ }
+
+ return ret;
+}
+
+REG_VAL *reg_key_get_value_by_name(REG_KEY *key, const char *name)
+{
+ int i, max;
+ REG_VAL *ret = NULL;
+
+ if(!key) return NULL;
+
+ if(key->handle->functions->get_value_by_name) {
+ ret = key->handle->functions->get_value_by_name(key,name);
+ } else {
+ max = reg_key_num_values(key);
+ for(i = 0; i < max; i++) {
+ REG_VAL *v = reg_key_get_value_by_index(key, i);
+ if(v && StrCaseCmp(v->name, name)) {
+ ret = v;
+ break;
+ }
+ reg_val_free(v);
+ }
+ }
+
+ if(ret) {
+ ret->parent = key;
+ ret->handle = key->handle;
+ }
+
+ return ret;
+}
+
+BOOL reg_key_del(REG_KEY *key)
+{
+ if(key->handle->functions->del_key)
+ return key->handle->functions->del_key(key);
+
+ return False;
+}
+
+BOOL reg_sync(REG_HANDLE *h, const char *location)
+{
+ if(!h->functions->sync)
+ return True;
+
+ return h->functions->sync(h, location);
+}
+
+BOOL reg_key_del_recursive(REG_KEY *key)
+{
+ BOOL succeed = True;
+ int i;
+
+ /* Delete all values for specified key */
+ for(i = 0; i < reg_key_num_values(key); i++) {
+ if(!reg_val_del(reg_key_get_value_by_index(key, i)))
+ succeed = False;
+ }
+
+ /* Delete all keys below this one */
+ for(i = 0; i < reg_key_num_subkeys(key); i++) {
+ if(!reg_key_del_recursive(reg_key_get_subkey_by_index(key, i)))
+ succeed = False;
+ }
+
+ if(succeed)reg_key_del(key);
+
+ return succeed;
+}
+
+BOOL reg_val_del(REG_VAL *val)
+{
+ if (!val->handle->functions->del_value) {
+ DEBUG(1, ("Backend '%s' doesn't support method del_value\n", val->handle->functions->name));
+ return False;
+ }
+
+ return val->handle->functions->del_value(val);
+}
+
+BOOL reg_key_add_name(REG_KEY *parent, const char *name)
+{
+ if (!parent->handle->functions->add_key) {
+ DEBUG(1, ("Backend '%s' doesn't support method add_key\n", parent->handle->functions->name));
+ return False;
+ }
+
+ return parent->handle->functions->add_key(parent, name);
+}
+
+BOOL reg_val_update(REG_VAL *val, int type, void *data, int len)
+{
+ /* A 'real' update function has preference */
+ if (val->handle->functions->update_value)
+ return val->handle->functions->update_value(val, type, data, len);
+
+ /* Otherwise, just remove and add again */
+ if (val->handle->functions->add_value &&
+ val->handle->functions->del_value) {
+ REG_VAL *new;
+ if(!val->handle->functions->del_value(val))
+ return False;
+
+ new = val->handle->functions->add_value(val->parent, val->name, type, data, len);
+ memcpy(val, new, sizeof(REG_VAL));
+ return True;
+ }
+
+ DEBUG(1, ("Backend '%s' doesn't support method update_value\n", val->handle->functions->name));
+ return False;
+}
+
+void reg_free(REG_HANDLE *h)
+{
+ if(!h->functions->close_registry) return;
+
+ h->functions->close_registry(h);
+}
+
+REG_KEY *reg_get_root(REG_HANDLE *h)
+{
+ REG_KEY *ret = NULL;
+ if(h->functions->open_root_key) {
+ ret = h->functions->open_root_key(h);
+ } else if(h->functions->open_key) {
+ ret = h->functions->open_key(h, "\\");
+ } else {
+ DEBUG(0, ("Backend '%s' has neither open_root_key nor open_key method implemented\n", h->functions->name));
+ }
+
+ if(ret) {
+ ret->handle = h;
+ ret->path = strdup("\\");
+ }
+
+ return ret;
+}
+
+REG_VAL *reg_key_add_value(REG_KEY *key, const char *name, int type, void *value, size_t vallen)
+{
+ REG_VAL *ret;
+ if(!key->handle->functions->add_value)
+ return NULL;
+
+ ret = key->handle->functions->add_value(key, name, type, value, vallen);
+ ret->parent = key;
+ ret->handle = key->handle;
+ return ret;
+}
diff --git a/source4/lib/registry/common/reg_objects.c b/source4/lib/registry/common/reg_objects.c
new file mode 100644
index 0000000000..911dc15c8e
--- /dev/null
+++ b/source4/lib/registry/common/reg_objects.c
@@ -0,0 +1,202 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Gerald Carter 2002.
+ * 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 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.
+ */
+
+/* Implementation of registry frontend view functions. */
+
+#include "includes.h"
+#include "lib/registry/common/registry.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+/***********************************************************************
+ allocate memory for and duplicate a REG_VAL.
+ This is malloc'd memory so the caller should free it when done
+ **********************************************************************/
+
+REG_VAL* reg_val_dup( REG_VAL *val )
+{
+ REG_VAL *copy = NULL;
+
+ if ( !val )
+ return NULL;
+
+ if ( !(copy = malloc( sizeof(REG_VAL) )) ) {
+ DEBUG(0,("dup_registry_value: malloc() failed!\n"));
+ return NULL;
+ }
+
+ /* copy all the non-pointer initial data */
+
+ memcpy( copy, val, sizeof(REG_VAL) );
+ if ( val->data_blk )
+ {
+ if ( !(copy->data_blk = memdup( val->data_blk, val->data_len )) ) {
+ DEBUG(0,("dup_registry_value: memdup() failed for [%d] bytes!\n",
+ val->data_len));
+ SAFE_FREE( copy );
+ }
+ }
+
+ return copy;
+}
+
+/**********************************************************************
+ free the memory allocated to a REG_VAL
+ *********************************************************************/
+
+void reg_val_free( REG_VAL *val )
+{
+ if ( !val )
+ return;
+
+ if(val->handle->functions->free_val_backend_data)
+ val->handle->functions->free_val_backend_data(val);
+
+ SAFE_FREE( val->data_blk );
+ SAFE_FREE( val );
+
+ return;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+uint8* reg_val_data_blk( REG_VAL *val )
+{
+ return val->data_blk;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+int reg_val_size( REG_VAL *val )
+{
+ return val->data_len;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+char *reg_val_name( REG_VAL *val )
+{
+ return val->name;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+uint32 reg_val_type( REG_VAL *val )
+{
+ return val->data_type;
+}
+
+/**********************************************************************
+ *********************************************************************/
+
+char *reg_key_name( REG_KEY *key )
+{
+ return key->name;
+}
+
+REG_KEY *reg_key_dup(REG_KEY *key)
+{
+ key->ref++;
+ return key;
+}
+
+void reg_key_free(REG_KEY *key)
+{
+ if(!key)
+ return;
+
+ key->ref--;
+ if(key->ref) return;
+
+ if(key->handle->functions->free_key_backend_data)
+ key->handle->functions->free_key_backend_data(key);
+
+ if(key->cache_values) {
+ int i;
+ for(i = 0; i < key->cache_values_count; i++) {
+ reg_val_free(key->cache_values[i]);
+ }
+ SAFE_FREE(key->cache_values);
+ }
+
+ if(key->cache_subkeys) {
+ int i;
+ for(i = 0; i < key->cache_subkeys_count; i++) {
+ reg_key_free(key->cache_subkeys[i]);
+ }
+ SAFE_FREE(key->cache_subkeys);
+ }
+
+ SAFE_FREE(key->path);
+ SAFE_FREE(key->name);
+ SAFE_FREE(key);
+}
+
+char *reg_val_get_path(REG_VAL *v)
+{
+ /* FIXME */
+ return NULL;
+}
+
+char *reg_key_get_path(REG_KEY *k)
+{
+ SMB_REG_ASSERT(k);
+ return k->path;
+}
+
+/* For use by the backends _ONLY_ */
+REG_KEY *reg_key_new_abs(const char *path, REG_HANDLE *h, void *data)
+{
+ REG_KEY *r = malloc(sizeof(REG_KEY));
+ ZERO_STRUCTP(r);
+ r->handle = h;
+ r->path = strdup(path);
+ r->name = strdup(strrchr(path, '\\')?strrchr(path,'\\')+1:path);
+ r->backend_data = data;
+ r->ref = 1;
+ return r;
+}
+
+REG_KEY *reg_key_new_rel(const char *name, REG_KEY *k, void *data)
+{
+ REG_KEY *r = malloc(sizeof(REG_KEY));
+ ZERO_STRUCTP(r);
+ r->handle = k->handle;
+ r->name = strdup(name);
+ r->backend_data = data;
+ r->ref = 1;
+ return r;
+}
+
+REG_VAL *reg_val_new(REG_KEY *parent, void *data)
+{
+ REG_VAL *r = malloc(sizeof(REG_VAL));
+ ZERO_STRUCTP(r);
+ r->handle = parent->handle;
+ r->backend_data = data;
+ r->ref = 1;
+ return r;
+}
diff --git a/source4/lib/registry/common/reg_util.c b/source4/lib/registry/common/reg_util.c
new file mode 100644
index 0000000000..2941c38cf4
--- /dev/null
+++ b/source4/lib/registry/common/reg_util.c
@@ -0,0 +1,164 @@
+/*
+ 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 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_REGISTRY
+
+/* Return string description of registry value type */
+const char *str_regtype(int type)
+{
+ switch(type) {
+ case REG_SZ: return "STRING";
+ case REG_DWORD: return "DWORD";
+ case REG_BINARY: return "BINARY";
+ }
+ return "Unknown";
+}
+
+char *reg_val_data_string(REG_VAL *v)
+{
+ char *asciip;
+ char *ret = NULL;
+ int i;
+
+ if(reg_val_size(v) == 0) return strdup("");
+
+ switch (reg_val_type(v)) {
+ case REG_SZ:
+ /* FIXME: Convert to ascii */
+ return strdup(reg_val_data_blk(v));
+
+ case REG_EXPAND_SZ:
+ return strdup(reg_val_data_blk(v));
+
+ case REG_BINARY:
+ ret = malloc(reg_val_size(v) * 3 + 2);
+ asciip = ret;
+ for (i=0; i<reg_val_size(v); i++) {
+ int str_rem = reg_val_size(v) * 3 - (asciip - ret);
+ asciip += snprintf(asciip, str_rem, "%02x", *(unsigned char *)(reg_val_data_blk(v)+i));
+ if (i < reg_val_size(v) && str_rem > 0)
+ *asciip = ' '; asciip++;
+ }
+ *asciip = '\0';
+ return ret;
+ break;
+
+ case REG_DWORD:
+ if (*(int *)reg_val_data_blk(v) == 0)
+ ret = strdup("0");
+ else
+ asprintf(&ret, "0x%x", *(int *)reg_val_data_blk(v));
+ break;
+
+ case REG_MULTI_SZ:
+ /* FIXME */
+ break;
+
+ default:
+ return 0;
+ break;
+ }
+
+ return ret;
+}
+
+const char *reg_val_description(REG_VAL *val)
+{
+ char *ret, *ds = reg_val_data_string(val);
+ asprintf(&ret, "%s = %s : %s", reg_val_name(val)?reg_val_name(val):"<No Name>", str_regtype(reg_val_type(val)), ds);
+ free(ds);
+ return ret;
+}
+
+BOOL reg_val_set_string(REG_VAL *val, char *str)
+{
+ /* FIXME */
+ return False;
+}
+
+REG_VAL *reg_key_get_subkey_val(REG_KEY *key, const char *subname, const char *valname)
+{
+ REG_KEY *k = reg_key_get_subkey_by_name(key, subname);
+ if(!k) return NULL;
+
+ return reg_key_get_value_by_name(k, valname);
+}
+
+BOOL reg_key_set_subkey_val(REG_KEY *key, const char *subname, const char *valname, uint32 type, uint8 *data, int real_len)
+{
+ REG_KEY *k = reg_key_get_subkey_by_name(key, subname);
+ REG_VAL *v;
+ if(!k) return False;
+
+ v = reg_key_get_value_by_name(k, valname);
+ if(!v) return False;
+
+ return reg_val_update(v, type, data, real_len);
+}
+
+/***********************************************************************
+ Utility function for splitting the base path of a registry path off
+ by setting base and new_path to the apprapriate offsets withing the
+ path.
+
+ WARNING!! Does modify the original string!
+ ***********************************************************************/
+
+BOOL reg_split_path( char *path, char **base, char **new_path )
+{
+ char *p;
+
+ *new_path = *base = NULL;
+
+ if ( !path)
+ return False;
+
+ *base = path;
+
+ p = strchr( path, '\\' );
+
+ if ( p ) {
+ *p = '\0';
+ *new_path = p+1;
+ }
+
+ return True;
+}
+
+char *reg_path_win2unix(char *path)
+{
+ int i;
+ for(i = 0; path[i]; i++) {
+ if(path[i] == '\\') path[i] = '/';
+ }
+ return path;
+}
+
+char *reg_path_unix2win(char *path)
+{
+ int i;
+ for(i = 0; path[i]; i++) {
+ if(path[i] == '/') path[i] = '\\';
+ }
+ return path;
+}
diff --git a/source4/lib/registry/common/registry.h b/source4/lib/registry/common/registry.h
new file mode 100644
index 0000000000..4b29006217
--- /dev/null
+++ b/source4/lib/registry/common/registry.h
@@ -0,0 +1,121 @@
+/*
+ Unix SMB/CIFS implementation.
+ Registry interface
+ This file contains the _internal_ structs for the registry
+ subsystem. Backends and the subsystem itself are the only
+ files that need to include this file.
+ Copyright (C) Gerald Carter 2002.
+ 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 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_REGISTRY_H /* _REGISTRY_REGISTRY_H */
+#define _REGISTRY_REGISTRY_H
+
+#define REGISTRY_INTERFACE_VERSION 1
+
+/* structure to store the registry handles */
+struct reg_key_s {
+ char *name; /* Name of the key */
+ char *path; /* Full path to the key */
+ smb_ucs2_t *class_name; /* Name of key class */
+ NTTIME last_mod; /* Time last modified */
+ SEC_DESC *security;
+ REG_HANDLE *handle;
+ void *backend_data;
+ REG_VAL **cache_values;
+ int cache_values_count;
+ REG_KEY **cache_subkeys;
+ int cache_subkeys_count;
+ int ref;
+};
+
+struct reg_val_s {
+ char *name;
+ int has_name;
+ int data_type;
+ int data_len;
+ void *data_blk; /* Might want a separate block */
+ REG_HANDLE *handle;
+ REG_KEY *parent;
+ void *backend_data;
+ int ref;
+};
+
+/*
+ * Container for function pointers to enumeration routines
+ * for virtual registry view
+ */
+
+struct reg_ops_s {
+ const char *name;
+ BOOL (*open_registry) (REG_HANDLE *, const char *location, BOOL try_complete_load);
+ BOOL (*sync)(REG_HANDLE *, const char *location);
+ BOOL (*close_registry) (REG_HANDLE *);
+
+ /* Either implement these */
+ REG_KEY *(*open_root_key) (REG_HANDLE *);
+ int (*num_subkeys) (REG_KEY *);
+ int (*num_values) (REG_KEY *);
+ REG_KEY *(*get_subkey_by_index) (REG_KEY *, int idx);
+ REG_KEY *(*get_subkey_by_name) (REG_KEY *, const char *name);
+ REG_VAL *(*get_value_by_index) (REG_KEY *, int idx);
+ REG_VAL *(*get_value_by_name) (REG_KEY *, const char *name);
+
+ /* Or these */
+ REG_KEY *(*open_key) (REG_HANDLE *, const char *name);
+ BOOL (*fetch_subkeys) (REG_KEY *, int *count, REG_KEY ***);
+ BOOL (*fetch_values) (REG_KEY *, int *count, REG_VAL ***);
+
+ /* Key management */
+ BOOL (*add_key)(REG_KEY *, const char *name);
+ BOOL (*del_key)(REG_KEY *);
+
+ /* Value management */
+ REG_VAL *(*add_value)(REG_KEY *, const char *name, int type, void *data, int len);
+ BOOL (*del_value)(REG_VAL *);
+
+ /* If update is not available, value will first be deleted and then added
+ * again */
+ BOOL (*update_value)(REG_VAL *, int type, void *data, int len);
+
+ void (*free_key_backend_data) (REG_KEY *);
+ void (*free_val_backend_data) (REG_VAL *);
+};
+
+typedef struct reg_sub_tree_s {
+ char *path;
+ REG_HANDLE *handle;
+ struct reg_sub_tree_s *prev, *next;
+} REG_SUBTREE;
+
+struct reg_handle_s {
+ REG_OPS *functions;
+ REG_SUBTREE *subtrees;
+ char *location;
+ void *backend_data;
+};
+
+struct reg_init_function_entry {
+ /* Function to create a member of the pdb_methods list */
+ REG_OPS *functions;
+ struct reg_init_function_entry *prev, *next;
+};
+
+/* Used internally */
+#define SMB_REG_ASSERT(a) { if(!(a)) { DEBUG(0,("%s failed! (%s:%d)", #a, __FILE__, __LINE__)); }}
+
+#endif /* _REGISTRY_H */
diff --git a/source4/lib/registry/config.m4 b/source4/lib/registry/config.m4
new file mode 100644
index 0000000000..277fd30dd1
--- /dev/null
+++ b/source4/lib/registry/config.m4
@@ -0,0 +1,16 @@
+# Registry backends
+
+if test t$BLDSHARED = ttrue; then
+ LIBWINREG_SHARED=bin/libwinregistry.$SHLIBEXT
+fi
+LIBWINREG=libwinregistry
+
+PKG_CHECK_MODULES(GCONF, gconf-2.0, [ SMB_MODULE_DEFAULT(reg_gconf,STATIC)
+ CFLAGS="$CFLAGS $GCONF_CFLAGS";], [AC_MSG_WARN([GConf not found, not building reg_gconf])])
+
+SMB_MODULE(reg_nt4, REG, STATIC, lib/registry/reg_backend_nt4/reg_backend_nt4.o)
+SMB_MODULE(reg_dir, REG, STATIC, lib/registry/reg_backend_dir/reg_backend_dir.o)
+SMB_MODULE(reg_rpc, REG, STATIC, lib/registry/reg_backend_rpc/reg_backend_rpc.o)
+SMB_MODULE(reg_gconf, REG, NOT, lib/registry/reg_backend_gconf/reg_backend_gconf.o, [], [$GCONF_LIBS])
+SMB_SUBSYSTEM(REG,lib/registry/common/reg_interface.o,[lib/registry/common/reg_objects.o lib/registry/common/reg_util.o],lib/registry/common/winregistry_proto.h,[])
+AC_OUTPUT(lib/registry/winregistry.pc)
diff --git a/source4/lib/registry/reg_backend_dir/reg_backend_dir.c b/source4/lib/registry/reg_backend_dir/reg_backend_dir.c
new file mode 100644
index 0000000000..baed39b4eb
--- /dev/null
+++ b/source4/lib/registry/reg_backend_dir/reg_backend_dir.c
@@ -0,0 +1,154 @@
+/*
+ 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 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/common/registry.h"
+
+static DIR *reg_dir_dir(REG_HANDLE *h, const char *base, const char *name)
+{
+ char *path = NULL;
+ DIR *d;
+ asprintf(&path, "%s/%s/%s", h->location, base, name);
+ path = reg_path_win2unix(path);
+
+ d = opendir(path);
+ if(!d) {
+ printf("Unable to open '%s'\n", path);
+ return NULL;
+ }
+ SAFE_FREE(path);
+ return d;
+}
+
+static BOOL reg_dir_add_key(REG_KEY *parent, const char *name)
+{
+ char *path;
+ int ret;
+ asprintf(&path, "%s/%s/%s", parent->handle->location, reg_key_get_path(parent), name);
+ path = reg_path_win2unix(path);
+ ret = mkdir(path, 0700);
+ free(path);
+ return (ret == 0);
+}
+
+static BOOL reg_dir_del_key(REG_KEY *k)
+{
+ char *path;
+ int ret;
+ asprintf(&path, "%s/%s", k->handle->location, reg_key_get_path(k));
+ path = reg_path_win2unix(path);
+ ret = rmdir(path);
+ free(path);
+ return (ret == 0);
+}
+
+static REG_KEY *reg_dir_open_key(REG_HANDLE *h, const char *name)
+{
+ DIR *d;
+ char *fullpath;
+ if(!name) {
+ DEBUG(0, ("NULL pointer passed as directory name!"));
+ return NULL;
+ }
+ fullpath = reg_path_win2unix(strdup(name));
+ d = reg_dir_dir(h, "", fullpath);
+ free(fullpath);
+
+ if(d) return reg_key_new_abs(name, h, d);
+ return NULL;
+}
+
+static BOOL reg_dir_fetch_subkeys(REG_KEY *k, int *count, REG_KEY ***r)
+{
+ DIR *d = (DIR *)k->backend_data;
+ struct dirent *e;
+ int max = 200;
+ REG_KEY **ar;
+ if(!d) return False;
+ rewinddir(d);
+ (*count) = 0;
+ ar = malloc(sizeof(REG_KEY *) * max);
+
+ while((e = readdir(d))) {
+ if(e->d_type == DT_DIR &&
+ strcmp(e->d_name, ".") &&
+ strcmp(e->d_name, "..")) {
+ char *fullpath = reg_path_win2unix(strdup(k->path));
+ ar[(*count)] = reg_key_new_rel(e->d_name, k, reg_dir_dir(k->handle, fullpath, e->d_name));
+ free(fullpath);
+ if(ar[(*count)])(*count)++;
+
+ if((*count) == max) {
+ max+=200;
+ ar = realloc(ar, sizeof(REG_KEY *) * max);
+ }
+ }
+ }
+
+ *r = ar;
+ return True;
+}
+
+static BOOL reg_dir_open(REG_HANDLE *h, const char *loc, BOOL try) {
+ if(!loc) return False;
+ return True;
+}
+
+static void dir_free(REG_KEY *k)
+{
+ closedir((DIR *)k->backend_data);
+}
+
+static REG_VAL *reg_dir_add_value(REG_KEY *p, const char *name, int type, void *data, int len)
+{
+ REG_VAL *ret = reg_val_new(p, NULL);
+ char *fullpath;
+ FILE *fd;
+ ret->name = name?strdup(name):NULL;
+ fullpath = reg_path_win2unix(strdup(reg_val_get_path(ret)));
+
+ fd = fopen(fullpath, "w+");
+
+ /* FIXME */
+ return NULL;
+}
+
+static BOOL reg_dir_del_value(REG_VAL *v)
+{
+ char *fullpath = reg_path_win2unix(strdup(reg_val_get_path(v)));
+ return False;
+}
+
+static REG_OPS reg_backend_dir = {
+ .name = "dir",
+ .open_registry = reg_dir_open,
+ .open_key = reg_dir_open_key,
+ .fetch_subkeys = reg_dir_fetch_subkeys,
+ .add_key = reg_dir_add_key,
+ .del_key = reg_dir_del_key,
+ .add_value = reg_dir_add_value,
+ .del_value = reg_dir_del_value,
+ .free_key_backend_data = dir_free
+};
+
+NTSTATUS reg_dir_init(void)
+{
+ return register_backend("registry", &reg_backend_dir);
+}
diff --git a/source4/lib/registry/reg_backend_gconf/reg_backend_gconf.c b/source4/lib/registry/reg_backend_gconf/reg_backend_gconf.c
new file mode 100644
index 0000000000..14da2f54e9
--- /dev/null
+++ b/source4/lib/registry/reg_backend_gconf/reg_backend_gconf.c
@@ -0,0 +1,189 @@
+/*
+ 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 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/common/registry.h"
+#include <gconf/gconf-client.h>
+
+static BOOL reg_open_gconf(REG_HANDLE *h, const char *location, BOOL try_complete_load)
+{
+ h->backend_data = (void *)gconf_client_get_default();
+ return True;
+}
+
+static BOOL reg_close_gconf(REG_HANDLE *h)
+{
+ return True;
+}
+
+static REG_KEY *gconf_open_key (REG_HANDLE *h, const char *name)
+{
+ char *fullpath = reg_path_win2unix(strdup(name));
+
+ /* Check if key exists */
+ if(!gconf_client_dir_exists((GConfClient *)h->backend_data, fullpath, NULL)) {
+ free(fullpath);
+ return NULL;
+ }
+ free(fullpath);
+
+ return reg_key_new_abs(name, h, NULL);
+}
+
+static BOOL gconf_fetch_values(REG_KEY *p, int *count, REG_VAL ***vals)
+{
+ GSList *entries;
+ GSList *cur;
+ REG_VAL **ar = malloc(sizeof(REG_VAL *));
+ char *fullpath = strdup(reg_key_get_path(p));
+ fullpath = reg_path_win2unix(fullpath);
+ cur = entries = gconf_client_all_entries((GConfClient*)p->handle->backend_data, fullpath, NULL);
+ free(fullpath);
+
+ (*count) = 0;
+ while(cur) {
+ GConfEntry *entry = cur->data;
+ GConfValue *value = gconf_entry_get_value(entry);
+ REG_VAL *newval = reg_val_new(p, NULL);
+ newval->name = strdup(strrchr(gconf_entry_get_key(entry), '/')+1);
+ if(value) {
+ switch(value->type) {
+ case GCONF_VALUE_INVALID:
+ newval->data_type = REG_NONE;
+ break;
+
+ case GCONF_VALUE_STRING:
+ newval->data_type = REG_SZ;
+ newval->data_blk = strdup(gconf_value_get_string(value));
+ newval->data_len = strlen(newval->data_blk);
+ break;
+
+ case GCONF_VALUE_INT:
+ newval->data_type = REG_DWORD;
+ newval->data_blk = malloc(sizeof(long));
+ *((long *)newval->data_blk) = gconf_value_get_int(value);
+ newval->data_len = sizeof(long);
+ break;
+
+ case GCONF_VALUE_FLOAT:
+ newval->data_blk = malloc(sizeof(double));
+ newval->data_type = REG_BINARY;
+ *((double *)newval->data_blk) = gconf_value_get_float(value);
+ newval->data_len = sizeof(double);
+ break;
+
+ case GCONF_VALUE_BOOL:
+ newval->data_blk = malloc(sizeof(BOOL));
+ newval->data_type = REG_BINARY;
+ *((BOOL *)newval->data_blk) = gconf_value_get_bool(value);
+ newval->data_len = sizeof(BOOL);
+ break;
+
+ default:
+ newval->data_type = REG_NONE;
+ DEBUG(0, ("Not implemented..\n"));
+ break;
+ }
+ } else newval->data_type = REG_NONE;
+
+ ar[(*count)] = newval;
+ ar = realloc(ar, sizeof(REG_VAL *) * ((*count)+2));
+ (*count)++;
+ g_free(cur->data);
+ cur = cur->next;
+ }
+
+ g_slist_free(entries);
+ *vals = ar;
+ return True;
+}
+
+static BOOL gconf_fetch_subkeys(REG_KEY *p, int *count, REG_KEY ***subs)
+{
+ GSList *dirs;
+ GSList *cur;
+ REG_KEY **ar = malloc(sizeof(REG_KEY *));
+ char *fullpath = strdup(reg_key_get_path(p));
+ fullpath = reg_path_win2unix(fullpath);
+ cur = dirs = gconf_client_all_dirs((GConfClient*)p->handle->backend_data, fullpath,NULL);
+ free(fullpath);
+
+ (*count) = 0;
+ while(cur) {
+ ar[(*count)] = reg_key_new_abs(reg_path_unix2win((char *)cur->data), p->handle, NULL);
+ ar = realloc(ar, sizeof(REG_KEY *) * ((*count)+2));
+ (*count)++;
+ g_free(cur->data);
+ cur = cur->next;
+ }
+
+ g_slist_free(dirs);
+ *subs = ar;
+ return True;
+}
+
+static BOOL gconf_update_value(REG_VAL *val, int type, void *data, int len)
+{
+ GError *error = NULL;
+ char *keypath = reg_path_win2unix(strdup(reg_key_get_path(val->parent)));
+ char *valpath;
+ if(val->name)asprintf(&valpath, "%s/%s", keypath, val->name);
+ else valpath = strdup(keypath);
+ free(keypath);
+
+ switch(type) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ gconf_client_set_string((GConfClient *)val->handle->backend_data, valpath, data, &error);
+ free(valpath);
+ return (error == NULL);
+
+ case REG_DWORD:
+ gconf_client_set_int((GConfClient *)val->handle->backend_data, valpath,
+ *((int *)data), &error);
+ free(valpath);
+ return (error == NULL);
+ default:
+ DEBUG(0, ("Unsupported type: %d\n", type));
+ free(valpath);
+ return False;
+ }
+ return False;
+}
+
+static REG_OPS reg_backend_gconf = {
+ .name = "gconf",
+ .open_registry = reg_open_gconf,
+ .close_registry = reg_close_gconf,
+ .open_key = gconf_open_key,
+ .fetch_subkeys = gconf_fetch_subkeys,
+ .fetch_values = gconf_fetch_values,
+ .update_value = gconf_update_value,
+
+ /* Note:
+ * since GConf uses schemas for what keys and values are allowed, there
+ * is no way of 'emulating' add_key and del_key here.
+ */
+};
+
+NTSTATUS reg_gconf_init(void)
+{
+ return register_backend("registry", &reg_backend_gconf);
+}
diff --git a/source4/lib/registry/reg_backend_ldb/reg_backend_ldb.c b/source4/lib/registry/reg_backend_ldb/reg_backend_ldb.c
new file mode 100644
index 0000000000..75d5a95187
--- /dev/null
+++ b/source4/lib/registry/reg_backend_ldb/reg_backend_ldb.c
@@ -0,0 +1,61 @@
+/*
+ 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 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/common/registry.h"
+
+/*
+ * Saves the dn as private_data for every key/val
+ */
+
+static BOOL ldb_open_registry(REG_HANDLE *handle, const char *location, BOOL try_full_load)
+{
+ struct ldb_context *c;
+ c = ldb_connect(location, 0, NULL);
+
+ if(!c) return False;
+
+ handle->backend_data = c;
+
+ return True;
+}
+
+static BOOL ldb_close_registry(REG_HANDLE *h)
+{
+ ldb_close(h);
+ return True;
+}
+
+static REG_KEY *ldb_open_key(REG_HANDLE *h, const char *name)
+{
+ /* FIXME */
+}
+
+static REG_OPS reg_backend_ldb = {
+ .name = "ldb",
+ .open_registry = ldb_open_registry,
+ .close_registry = ldb_close_registry,
+ .open_key = ldb_open_key,
+};
+
+NTSTATUS reg_ldb_init(void)
+{
+ return register_backend("registry", &reg_backend_ldb);
+}
diff --git a/source4/lib/registry/reg_backend_nt4/reg_backend_nt4.c b/source4/lib/registry/reg_backend_nt4/reg_backend_nt4.c
new file mode 100644
index 0000000000..ef2565bda8
--- /dev/null
+++ b/source4/lib/registry/reg_backend_nt4/reg_backend_nt4.c
@@ -0,0 +1,1745 @@
+/*
+ Samba Unix/Linux SMB client utility libeditreg.c
+ Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
+ Copyright (C) 2003-2004 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. */
+
+/*************************************************************************
+
+ A utility to edit a Windows NT/2K etc registry file.
+
+ Many of the ideas in here come from other people and software.
+ I first looked in Wine in misc/registry.c and was also influenced by
+ http://www.wednesday.demon.co.uk/dosreg.html
+
+ Which seems to contain comments from someone else. I reproduce them here
+ incase the site above disappears. It actually comes from
+ http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt.
+
+ The goal here is to read the registry into memory, manipulate it, and then
+ write it out if it was changed by any actions of the user.
+
+The windows NT registry has 2 different blocks, where one can occur many
+times...
+
+the "regf"-Block
+================
+
+"regf" is obviously the abbreviation for "Registry file". "regf" is the
+signature of the header-block which is always 4kb in size, although only
+the first 64 bytes seem to be used and a checksum is calculated over
+the first 0x200 bytes only!
+
+Offset Size Contents
+0x00000000 D-Word ID: ASCII-"regf" = 0x66676572
+0x00000004 D-Word ???? //see struct REG_HANDLE
+0x00000008 D-Word ???? Always the same value as at 0x00000004
+0x0000000C Q-Word last modify date in WinNT date-format
+0x00000014 D-Word 1
+0x00000018 D-Word 3
+0x0000001C D-Word 0
+0x00000020 D-Word 1
+0x00000024 D-Word Offset of 1st key record
+0x00000028 D-Word Size of the data-blocks (Filesize-4kb)
+0x0000002C D-Word 1
+0x000001FC D-Word Sum of all D-Words from 0x00000000 to
+0x000001FB //XOR of all words. Nigel
+
+I have analyzed more registry files (from multiple machines running
+NT 4.0 german version) and could not find an explanation for the values
+marked with ???? the rest of the first 4kb page is not important...
+
+the "hbin"-Block
+================
+I don't know what "hbin" stands for, but this block is always a multiple
+of 4kb in size.
+
+Inside these hbin-blocks the different records are placed. The memory-
+management looks like a C-compiler heap management to me...
+
+hbin-Header
+===========
+Offset Size Contents
+0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268
+0x0004 D-Word Offset from the 1st hbin-Block
+0x0008 D-Word Offset to the next hbin-Block
+0x001C D-Word Block-size
+
+The values in 0x0008 and 0x001C should be the same, so I don't know
+if they are correct or swapped...
+
+From offset 0x0020 inside a hbin-block data is stored with the following
+format:
+
+Offset Size Contents
+0x0000 D-Word Data-block size //this size must be a
+multiple of 8. Nigel
+0x0004 ???? Data
+
+If the size field is negative (bit 31 set), the corresponding block
+is free and has a size of -blocksize!
+
+That does not seem to be true. All block lengths seem to be negative!
+(Richard Sharpe)
+
+The data is stored as one record per block. Block size is a multiple
+of 4 and the last block reaches the next hbin-block, leaving no room.
+
+(That also seems incorrect, in that the block size if a multiple of 8.
+That is, the block, including the 4 byte header, is always a multiple of
+8 bytes. Richard Sharpe.)
+
+Records in the hbin-blocks
+==========================
+
+nk-Record
+
+ The nk-record can be treated as a combination of tree-record and
+ key-record of the win 95 registry.
+
+lf-Record
+
+ The lf-record is the counterpart to the RGKN-record (the
+ hash-function)
+
+vk-Record
+
+ The vk-record consists information to a single value (value key).
+
+sk-Record
+
+ sk (? Security Key ?) is the ACL of the registry.
+
+Value-Lists
+
+ The value-lists contain information about which values are inside a
+ sub-key and don't have a header.
+
+Datas
+
+ The datas of the registry are (like the value-list) stored without a
+ header.
+
+All offset-values are relative to the first hbin-block and point to the
+block-size field of the record-entry. to get the file offset, you have to add
+the header size (4kb) and the size field (4 bytes)...
+
+the nk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"nk" = 0x6B6E
+0x0002 Word for the root-key: 0x2C, otherwise 0x20 //key symbolic links 0x10. Nigel
+0x0004 Q-Word write-date/time in windows nt notation
+0x0010 D-Word Offset of Owner/Parent key
+0x0014 D-Word number of sub-Keys
+0x001C D-Word Offset of the sub-key lf-Records
+0x0024 D-Word number of values
+0x0028 D-Word Offset of the Value-List
+0x002C D-Word Offset of the sk-Record
+
+0x0030 D-Word Offset of the Class-Name //see NK structure for the use of these fields. Nigel
+0x0044 D-Word Unused (data-trash) //some kind of run time index. Does not appear to be important. Nigel
+0x0048 Word name-length
+0x004A Word class-name length
+0x004C ???? key-name
+
+the Value-List
+==============
+Offset Size Contents
+0x0000 D-Word Offset 1st Value
+0x0004 D-Word Offset 2nd Value
+0x???? D-Word Offset nth Value
+
+To determine the number of values, you have to look at the owner-nk-record!
+
+Der vk-Record
+=============
+Offset Size Contents
+0x0000 Word ID: ASCII-"vk" = 0x6B76
+0x0002 Word name length
+0x0004 D-Word length of the data //if top bit is set when offset contains data. Nigel
+0x0008 D-Word Offset of Data
+0x000C D-Word Type of value
+0x0010 Word Flag
+0x0012 Word Unused (data-trash)
+0x0014 ???? Name
+
+If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
+
+If the data-size is lower 5, the data-offset value is used to store the data itself!
+
+The data-types
+==============
+Wert Beteutung
+0x0001 RegSZ: character string (in UNICODE!)
+0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!)
+0x0003 RegBin: raw-binary value
+0x0004 RegDWord: Dword
+0x0007 RegMultiSZ: multiple strings, seperated with 0
+ (UNICODE!)
+
+The "lf"-record
+===============
+Offset Size Contents
+0x0000 Word ID: ASCII-"lf" = 0x666C
+0x0002 Word number of keys
+0x0004 ???? Hash-Records
+
+Hash-Record
+===========
+Offset Size Contents
+0x0000 D-Word Offset of corresponding "nk"-Record
+0x0004 D-Word ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv!
+
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+(This is just a self-relative security descriptor in the data. R Sharpe.)
+
+
+Offset Size Contents
+0x0000 Word ID: ASCII-"sk" = 0x6B73
+0x0002 Word Unused
+0x0004 D-Word Offset of previous "sk"-Record
+0x0008 D-Word Offset of next "sk"-Record
+0x000C D-Word usage-counter
+0x0010 D-Word Size of "sk"-record in bytes
+???? //standard self
+relative security desciptor. Nigel
+???? ???? Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I don't know how accurate it realy is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+ +----->+------------+
+| first sub | | | | Usage cnt. |
+| name | | +-->+------------+ | | length |
+| value | | | | next rec. | | | text |------->+-------+
++-----------+ | | | name rec. |--+ +------------+ | xxxxx |
+ +------------+ | | value rec. |-------->+------------+ +-------+
+ v | +------------+ | Usage cnt. |
++-----------+ | | length |
+| next rec. | | | text |------->+-------+
+| first sub |------+ +------------+ | xxxxx |
+| name | +-------+
+| value |
++-----------+
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+
++---------------------------------------------------------------+
+| |
+v |
++---------+ +---------->+-----------+ +----->+---------+ |
+| "nk" | | | lf-rec. | | | nk-rec. | |
+| ID | | | # of keys | | | parent |---+
+| Date | | | 1st key |--+ | .... |
+| parent | | +-----------+ +---------+
+| suk-keys|-----+
+| values |--------------------->+----------+
+| SK-rec. |---------------+ | 1. value |--> +----------+
+| class |--+ | +----------+ | vk-rec. |
++---------+ | | | .... |
+ v | | data |--> +-------+
+ +------------+ | +----------+ | xxxxx |
+ | Class name | | +-------+
+ +------------+ |
+ v
+ +---------+ +---------+
+ +----->| next sk |--->| Next sk |--+
+ | +---| prev sk |<---| prev sk | |
+ | | | .... | | ... | |
+ | | +---------+ +---------+ |
+ | | ^ |
+ | | | |
+ | +--------------------+ |
+ +----------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps.... (Although it was "fun" for me to uncover this things,
+ it took me several sleepless nights ;)
+
+ B.D.
+
+*************************************************************************/
+
+#include "includes.h"
+#include "lib/registry/common/registry.h"
+
+#define REG_KEY_LIST_SIZE 10
+/*FIXME*/
+
+/*
+ * Structures for dealing with the on-disk format of the registry
+ */
+
+const char *def_owner_sid_str = NULL;
+
+/*
+ * These definitions are for the in-memory registry structure.
+ * It is a tree structure that mimics what you see with tools like regedit
+ */
+
+
+/*
+ * Definition of a Key. It has a name, classname, date/time last modified,
+ * sub-keys, values, and a security descriptor
+ */
+
+#define REG_ROOT_KEY 1
+#define REG_SUB_KEY 2
+#define REG_SYM_LINK 3
+
+/*
+ * All of the structures below actually have a four-byte length before them
+ * which always seems to be negative. The following macro retrieves that
+ * size as an integer
+ */
+
+#define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
+
+typedef unsigned int DWORD;
+typedef unsigned short WORD;
+
+typedef struct sk_struct SK_HDR;
+/*
+ * This structure keeps track of the output format of the registry
+ */
+#define REG_OUTBLK_HDR 1
+#define REG_OUTBLK_HBIN 2
+
+typedef struct regf_block {
+ DWORD REGF_ID; /* regf */
+ DWORD uk1;
+ DWORD uk2;
+ DWORD tim1, tim2;
+ DWORD uk3; /* 1 */
+ DWORD uk4; /* 3 */
+ DWORD uk5; /* 0 */
+ DWORD uk6; /* 1 */
+ DWORD first_key; /* offset */
+ unsigned int dblk_size;
+ DWORD uk7[116]; /* 1 */
+ DWORD chksum;
+} REGF_HDR;
+
+typedef struct hbin_sub_struct {
+ DWORD dblocksize;
+ char data[1];
+} HBIN_SUB_HDR;
+
+typedef struct hbin_struct {
+ DWORD HBIN_ID; /* hbin */
+ DWORD off_from_first;
+ DWORD off_to_next;
+ DWORD uk1;
+ DWORD uk2;
+ DWORD uk3;
+ DWORD uk4;
+ DWORD blk_size;
+ HBIN_SUB_HDR hbin_sub_hdr;
+} HBIN_HDR;
+
+typedef struct nk_struct {
+ WORD NK_ID;
+ WORD type;
+ DWORD t1, t2;
+ DWORD uk1;
+ DWORD own_off;
+ DWORD subk_num;
+ DWORD uk2;
+ DWORD lf_off;
+ DWORD uk3;
+ DWORD val_cnt;
+ DWORD val_off;
+ DWORD sk_off;
+ DWORD clsnam_off;
+ DWORD unk4[4];
+ DWORD unk5;
+ WORD nam_len;
+ WORD clsnam_len;
+ char key_nam[1]; /* Actual length determined by nam_len */
+} NK_HDR;
+
+struct sk_struct {
+ WORD SK_ID;
+ WORD uk1;
+ DWORD prev_off;
+ DWORD next_off;
+ DWORD ref_cnt;
+ DWORD rec_size;
+ char sec_desc[1];
+};
+
+typedef struct key_sec_desc_s {
+ struct key_sec_desc_s *prev, *next;
+ int ref_cnt;
+ int state;
+ int offset;
+ SK_HDR *sk_hdr; /* This means we must keep the registry in memory */
+ SEC_DESC *sec_desc;
+} KEY_SEC_DESC;
+
+/* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
+typedef struct sk_map_s {
+ int sk_off;
+ KEY_SEC_DESC *key_sec_desc;
+} SK_MAP;
+
+typedef struct vk_struct {
+ WORD VK_ID;
+ WORD nam_len;
+ DWORD dat_len; /* If top-bit set, offset contains the data */
+ DWORD dat_off;
+ DWORD dat_type;
+ WORD flag; /* =1, has name, else no name (=Default). */
+ WORD unk1;
+ char dat_name[1]; /* Name starts here ... */
+} VK_HDR;
+
+typedef DWORD VL_TYPE[1]; /* Value list is an array of vk rec offsets */
+
+typedef struct hash_struct {
+ DWORD nk_off;
+ char hash[4];
+} HASH_REC;
+
+
+typedef struct lf_struct {
+ WORD LF_ID;
+ WORD key_count;
+ struct hash_struct hr[1]; /* Array of hash records, depending on key_count */} LF_HDR;
+
+
+
+/*
+ * This structure keeps track of the output format of the registry
+ */
+#define REG_OUTBLK_HDR 1
+#define REG_OUTBLK_HBIN 2
+
+typedef struct hbin_blk_s {
+ int type, size;
+ struct hbin_blk_s *next;
+ char *data; /* The data block */
+ unsigned int file_offset; /* Offset in file */
+ unsigned int free_space; /* Amount of free space in block */
+ unsigned int fsp_off; /* Start of free space in block */
+ int complete, stored;
+} HBIN_BLK;
+
+typedef struct regf_struct_s {
+ int reg_type;
+ int fd;
+ struct stat sbuf;
+ char *base;
+ int modified;
+ NTTIME last_mod_time;
+ NK_HDR *first_key;
+ int sk_count, sk_map_size;
+ SK_MAP *sk_map;
+ const char *owner_sid_str;
+ SEC_DESC *def_sec_desc;
+ /*
+ * These next pointers point to the blocks used to contain the
+ * keys when we are preparing to write them to a file
+ */
+ HBIN_BLK *blk_head, *blk_tail, *free_space;
+ TALLOC_CTX *mem_ctx;
+} REGF;
+
+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
+
+/*
+ * Create an ACE
+ */
+static BOOL nt_create_ace(SEC_ACE *ace, int type, int flags, uint32 perms, const char *sid)
+{
+ DOM_SID s;
+ SEC_ACCESS access;
+ access.mask = perms;
+ if(!string_to_sid(&s, sid))return False;
+ init_sec_ace(ace, &s, type, access, flags);
+ return True;
+}
+
+/*
+ * Create a default ACL
+ */
+static SEC_ACL *nt_create_default_acl(REG_HANDLE *regf)
+{
+ SEC_ACE aces[8];
+
+ if(!nt_create_ace(&aces[0], 0x00, 0x0, 0xF003F, regf->owner_sid_str)) return NULL;
+ if(!nt_create_ace(&aces[1], 0x00, 0x0, 0xF003F, "S-1-5-18")) return NULL;
+ if(!nt_create_ace(&aces[2], 0x00, 0x0, 0xF003F, "S-1-5-32-544")) return NULL;
+ if(!nt_create_ace(&aces[3], 0x00, 0x0, 0x20019, "S-1-5-12")) return NULL;
+ if(!nt_create_ace(&aces[4], 0x00, 0x0B, GENERIC_RIGHT_ALL_ACCESS, regf->owner_sid_str)) return NULL;
+ if(!nt_create_ace(&aces[5], 0x00, 0x0B, 0x10000000, "S-1-5-18")) return NULL;
+ if(!nt_create_ace(&aces[6], 0x00, 0x0B, 0x10000000, "S-1-5-32-544")) return NULL;
+ if(!nt_create_ace(&aces[7], 0x00, 0x0B, 0x80000000, "S-1-5-12")) return NULL;
+
+ return make_sec_acl(regf->mem_ctx, 2, 8, aces);
+}
+
+/*
+ * Create a default security descriptor. We pull in things from env
+ * if need be
+ */
+static SEC_DESC *nt_create_def_sec_desc(REG_HANDLE *regf)
+{
+ SEC_DESC *tmp;
+
+ tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
+
+ tmp->revision = 1;
+ tmp->type = SEC_DESC_SELF_RELATIVE | SEC_DESC_DACL_PRESENT;
+ if (!string_to_sid(tmp->owner_sid, "S-1-5-32-544")) goto error;
+ if (!string_to_sid(tmp->grp_sid, "S-1-5-18")) goto error;
+ tmp->sacl = NULL;
+ tmp->dacl = nt_create_default_acl(regf);
+
+ return tmp;
+
+ error:
+ if (tmp) nt_delete_sec_desc(tmp);
+ return NULL;
+}
+
+/*
+ * We will implement inheritence that is based on what the parent's SEC_DESC
+ * says, but the Owner and Group SIDs can be overwridden from the command line
+ * and additional ACEs can be applied from the command line etc.
+ */
+static KEY_SEC_DESC *nt_inherit_security(REG_KEY *key)
+{
+
+ if (!key) return NULL;
+ return key->security;
+}
+
+/*
+ * Create an initial security descriptor and init other structures, if needed
+ * We assume that the initial security stuff is empty ...
+ */
+static KEY_SEC_DESC *nt_create_init_sec(REG_HANDLE *h)
+{
+ REGF *regf = h->backend_data;
+ KEY_SEC_DESC *tsec = NULL;
+
+ tsec = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+
+ tsec->ref_cnt = 1;
+ tsec->state = SEC_DESC_NBK;
+ tsec->offset = 0;
+
+ tsec->sec_desc = regf->def_sec_desc;
+
+ return tsec;
+}
+#endif
+
+/*
+ * Get the starting record for NT Registry file
+ */
+
+/*
+ * Where we keep all the regf stuff for one registry.
+ * This is the structure that we use to tie the in memory tree etc
+ * together. By keeping separate structs, we can operate on different
+ * registries at the same time.
+ * Currently, the SK_MAP is an array of mapping structure.
+ * Since we only need this on input and output, we fill in the structure
+ * as we go on input. On output, we know how many SK items we have, so
+ * we can allocate the structure as we need to.
+ * If you add stuff here that is dynamically allocated, add the
+ * appropriate free statements below.
+ */
+
+#define REG_HANDLE_REGTYPE_NONE 0
+#define REG_HANDLE_REGTYPE_NT 1
+#define REG_HANDLE_REGTYPE_W9X 2
+
+#define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
+ (r)->last_mod_time.high = (t2);
+
+#define REGF_HDR_BLKSIZ 0x1000
+
+#define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4)
+#define LOCN(base, f) ((base) + OFF(f))
+
+/* Get the header of the registry. Return a pointer to the structure
+ * If the mmap'd area has not been allocated, then mmap the input file
+ */
+static REGF_HDR *nt_get_regf_hdr(REG_HANDLE *h)
+{
+ REGF *regf = h->backend_data;
+ SMB_REG_ASSERT(regf);
+
+ if (!regf->base) { /* Try to mmap etc the file */
+
+ if ((regf->fd = open(h->location, O_RDONLY, 0000)) <0) {
+ return NULL; /* What about errors? */
+ }
+
+ if (fstat(regf->fd, &regf->sbuf) < 0) {
+ return NULL;
+ }
+
+ regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
+
+ if ((int)regf->base == 1) {
+ DEBUG(0,("Could not mmap file: %s, %s\n", h->location,
+ strerror(errno)));
+ return NULL;
+ }
+ }
+
+ /*
+ * At this point, regf->base != NULL, and we should be able to read the
+ * header
+ */
+
+ SMB_REG_ASSERT(regf->base != NULL);
+
+ return (REGF_HDR *)regf->base;
+}
+
+/*
+ * Validate a regf header
+ * For now, do nothing, but we should check the checksum
+ */
+static int valid_regf_hdr(REGF_HDR *regf_hdr)
+{
+ if (!regf_hdr) return 0;
+
+ return 1;
+}
+
+#if 0
+
+/*
+ * Process an SK header ...
+ * Every time we see a new one, add it to the map. Otherwise, just look it up.
+ * We will do a simple linear search for the moment, since many KEYs have the
+ * same security descriptor.
+ * We allocate the map in increments of 10 entries.
+ */
+
+/*
+ * Create a new entry in the map, and increase the size of the map if needed
+ */
+static SK_MAP *alloc_sk_map_entry(REG_HANDLE *h, KEY_SEC_DESC *tmp, int sk_off)
+{
+ REGF *regf = h->backend_data;
+ if (!regf->sk_map) { /* Allocate a block of 10 */
+ regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
+ regf->sk_map_size = 10;
+ regf->sk_count = 1;
+ (regf->sk_map)[0].sk_off = sk_off;
+ (regf->sk_map)[0].key_sec_desc = tmp;
+ }
+ else { /* Simply allocate a new slot, unless we have to expand the list */
+ int ndx = regf->sk_count;
+ if (regf->sk_count >= regf->sk_map_size) {
+ regf->sk_map = (SK_MAP *)realloc(regf->sk_map,
+ (regf->sk_map_size + 10)*sizeof(SK_MAP));
+ if (!regf->sk_map) {
+ free(tmp);
+ return NULL;
+ }
+ /*
+ * ndx already points at the first entry of the new block
+ */
+ regf->sk_map_size += 10;
+ }
+ (regf->sk_map)[ndx].sk_off = sk_off;
+ (regf->sk_map)[ndx].key_sec_desc = tmp;
+ regf->sk_count++;
+ }
+ return regf->sk_map;
+}
+
+/*
+ * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
+ * found
+ */
+KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
+{
+ int i;
+
+ if (!sk_map) return NULL;
+
+ for (i = 0; i < count; i++) {
+
+ if (sk_map[i].sk_off == sk_off)
+ return sk_map[i].key_sec_desc;
+
+ }
+
+ return NULL;
+
+}
+
+/*
+ * Allocate a KEY_SEC_DESC if we can't find one in the map
+ */
+static KEY_SEC_DESC *lookup_create_sec_key(REG_HANDLE *h, SK_MAP *sk_map, int sk_off)
+{
+ REGF *regf = h->backend_data;
+ KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
+
+ if (tmp) {
+ return tmp;
+ }
+ else { /* Allocate a new one */
+ tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+ memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */
+ tmp->state = SEC_DESC_RES;
+ if (!alloc_sk_map_entry(h, tmp, sk_off)) {
+ return NULL;
+ }
+ return tmp;
+ }
+}
+
+static SEC_DESC *process_sec_desc(REG_HANDLE *regf, SEC_DESC *sec_desc)
+{
+ SEC_DESC *tmp = NULL;
+
+ tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
+
+ tmp->revision = SVAL(&sec_desc->revision,0);
+ tmp->type = SVAL(&sec_desc->type,0);
+ DEBUG(2, ("SEC_DESC Rev: %0X, Type: %0X\n", tmp->revision, tmp->type));
+ DEBUGADD(2, ("SEC_DESC Owner Off: %0X\n", IVAL(&sec_desc->off_owner_sid,0)));
+ DEBUGADD(2, ("SEC_DESC Group Off: %0X\n", IVAL(&sec_desc->off_grp_sid,0)));
+ DEBUGADD(2, ("SEC_DESC DACL Off: %0X\n", IVAL(&sec_desc->off_dacl,0)));
+ tmp->owner_sid = sid_dup_talloc(regf->mem_ctx, (DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->off_owner_sid,0)));
+ if (!tmp->owner_sid) {
+ free(tmp);
+ return NULL;
+ }
+ tmp->grp_sid = sid_dup_talloc(regf->mem_ctx, (DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->off_grp_sid,0)));
+ if (!tmp->grp_sid) {
+ free(tmp);
+ return NULL;
+ }
+
+ /* Now pick up the SACL and DACL */
+
+ DEBUG(0, ("%d, %d\n", IVAL(&sec_desc->off_sacl,0), IVAL(&sec_desc->off_dacl,0)));
+
+ if (sec_desc->off_sacl)
+ tmp->sacl = dup_sec_acl(regf->mem_ctx, (SEC_ACL *)((char *)sec_desc + IVAL(&sec_desc->off_sacl,0)));
+ else
+ tmp->sacl = NULL;
+
+ if (sec_desc->off_dacl)
+ tmp->dacl = dup_sec_acl(regf->mem_ctx, (SEC_ACL *)((char *)sec_desc + IVAL(&sec_desc->off_dacl,0)));
+ else
+ tmp->dacl = NULL;
+
+ return tmp;
+}
+
+static KEY_SEC_DESC *process_sk(REG_HANDLE *regf, SK_HDR *sk_hdr, int sk_off, int size)
+{
+ KEY_SEC_DESC *tmp = NULL;
+ int sk_next_off, sk_prev_off, sk_size;
+ SEC_DESC *sec_desc;
+
+ if (!sk_hdr) return NULL;
+
+ if (SVAL(&sk_hdr->SK_ID,0) != str_to_dword("sk")) {
+ DEBUG(0, ("Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
+ regf->regfile_name));
+ return NULL;
+ }
+
+ if (-size < (sk_size = IVAL(&sk_hdr->rec_size,0))) {
+ DEBUG(0, ("Incorrect SK record size: %d vs %d. %s\n",
+ -size, sk_size, regf->regfile_name));
+ return NULL;
+ }
+
+ /*
+ * Now, we need to look up the SK Record in the map, and return it
+ * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
+ * use that
+ */
+
+ if (regf->sk_map &&
+ ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
+ && (tmp->state == SEC_DESC_OCU)) {
+ tmp->ref_cnt++;
+ return tmp;
+ }
+
+ /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
+
+ SMB_REG_ASSERT(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
+
+ /*
+ * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
+ * new KEY_SEC_DESC to the mapping structure, since the offset supplied is
+ * the actual offset of structure. The same offset will be used by
+ * all future references to this structure
+ * We could put all this unpleasantness in a function.
+ */
+
+ if (!tmp) {
+ tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+ memset(tmp, 0, sizeof(KEY_SEC_DESC));
+
+ /*
+ * Allocate an entry in the SK_MAP ...
+ * We don't need to free tmp, because that is done for us if the
+ * sm_map entry can't be expanded when we need more space in the map.
+ */
+
+ if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
+ return NULL;
+ }
+ }
+
+ tmp->ref_cnt++;
+ tmp->state = SEC_DESC_OCU;
+
+ /*
+ * Now, process the actual sec desc and plug the values in
+ */
+
+ sec_desc = (SEC_DESC *)&sk_hdr->sec_desc[0];
+ tmp->sec_desc = process_sec_desc(regf, sec_desc);
+
+ /*
+ * Now forward and back links. Here we allocate an entry in the sk_map
+ * if it does not exist, and mark it reserved
+ */
+
+ sk_prev_off = IVAL(&sk_hdr->prev_off,0);
+ tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
+ SMB_REG_ASSERT(tmp->prev != NULL);
+ sk_next_off = IVAL(&sk_hdr->next_off,0);
+ tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
+ SMB_REG_ASSERT(tmp->next != NULL);
+
+ return tmp;
+}
+#endif
+
+/*
+ * Process a VK header and return a value
+ */
+static REG_VAL *vk_to_val(REG_KEY *parent, VK_HDR *vk_hdr, int size)
+{
+ char val_name[1024];
+ REGF *regf = parent->handle->backend_data;
+ int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
+ const char *val_type;
+ REG_VAL *tmp = NULL;
+
+ if (!vk_hdr) return NULL;
+
+ if ((vk_id = SVAL(&vk_hdr->VK_ID,0)) != str_to_dword("vk")) {
+ DEBUG(0, ("Unrecognized VK header ID: %0X, block: %0X, %s\n",
+ vk_id, (int)vk_hdr, parent->handle->location));
+ return NULL;
+ }
+
+ nam_len = SVAL(&vk_hdr->nam_len,0);
+ val_name[nam_len] = '\0';
+ flag = SVAL(&vk_hdr->flag,0);
+ dat_type = IVAL(&vk_hdr->dat_type,0);
+ dat_len = IVAL(&vk_hdr->dat_len,0); /* If top bit, offset contains data */
+ dat_off = IVAL(&vk_hdr->dat_off,0);
+
+ tmp = reg_val_new(parent, NULL);
+ tmp->has_name = flag;
+ tmp->data_type = dat_type;
+
+ if (flag & 0x01) {
+ strncpy(val_name, vk_hdr->dat_name, nam_len);
+ tmp->name = strdup(val_name);
+ }
+ else
+ strncpy(val_name, "<No Name>", 10);
+
+ /*
+ * Allocate space and copy the data as a BLOB
+ */
+
+ if (dat_len&0x7FFFFFFF) {
+
+ char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
+
+ tmp->data_blk = dtmp;
+
+ if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */
+ char *dat_ptr = LOCN(regf->base, dat_off);
+ memcpy(dtmp, dat_ptr, dat_len);
+ }
+ else { /* The data is in the offset or type */
+ /*
+ * FIXME.
+ * Some registry files seem to have weird fields. If top bit is set,
+ * but len is 0, the type seems to be the value ...
+ * Not sure how to handle this last type for the moment ...
+ */
+ dat_len = dat_len & 0x7FFFFFFF;
+ memcpy(dtmp, &dat_off, dat_len);
+ }
+
+ tmp->data_len = dat_len;
+ }
+
+ return tmp;
+}
+
+static BOOL vl_verify(VL_TYPE vl, int count, int size)
+{
+ if(!vl) return False;
+ if (-size < (count+1)*sizeof(int)){
+ DEBUG(0, ("Error in VL header format. Size less than space required. %d\n", -size));
+ return False;
+ }
+ return True;
+}
+
+static BOOL lf_verify(REG_HANDLE *h, LF_HDR *lf_hdr, int size)
+{
+ int lf_id;
+ if ((lf_id = SVAL(&lf_hdr->LF_ID,0)) != str_to_dword("lf")) {
+ DEBUG(0, ("Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
+ lf_id, (int)lf_hdr, h->location));
+ return False;
+ }
+ return True;
+}
+
+static int lf_num_entries(REG_HANDLE *h, LF_HDR *lf_hdr, int size)
+{
+ int count;
+
+ if(!lf_verify(h, lf_hdr, size)) return 0;
+
+ SMB_REG_ASSERT(size < 0);
+
+ count = SVAL(&lf_hdr->key_count,0);
+ DEBUG(2, ("Key Count: %u\n", count));
+ if (count <= 0) return 0;
+
+ return count;
+}
+
+
+static REG_KEY *nk_to_key(REG_HANDLE *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent);
+
+
+
+/*
+ * Process an LF Header and return a list of sub-keys
+ */
+static REG_KEY *lf_get_entry(REG_KEY *parent, LF_HDR *lf_hdr, int size, int n)
+{
+ REGF *regf = parent->handle->backend_data;
+ int count, nk_off;
+ NK_HDR *nk_hdr;
+
+ if (!lf_hdr) return NULL;
+
+ if(!lf_verify(parent->handle, lf_hdr, size)) return NULL;
+
+ SMB_REG_ASSERT(size < 0);
+
+ count = SVAL(&lf_hdr->key_count,0);
+ DEBUG(2, ("Key Count: %u\n", count));
+ if (count <= 0 || n > count) return NULL;
+
+ nk_off = IVAL(&lf_hdr->hr[n].nk_off,0);
+ DEBUG(2, ("NK Offset: %0X\n", nk_off));
+ nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
+ return nk_to_key(parent->handle, nk_hdr, BLK_SIZE(nk_hdr), parent);
+}
+
+static REG_KEY *nk_to_key(REG_HANDLE *h, NK_HDR *nk_hdr, int size, REG_KEY *parent)
+{
+ REGF *regf = h->backend_data;
+ REG_KEY *tmp = NULL, *own;
+ int name_len, clsname_len, sk_off, own_off;
+ unsigned int nk_id;
+ SK_HDR *sk_hdr;
+ int type;
+ char key_name[1024], cls_name[1024];
+
+ if (!nk_hdr) return NULL;
+
+ if ((nk_id = SVAL(&nk_hdr->NK_ID,0)) != str_to_dword("nk")) {
+ DEBUG(0, ("Unrecognized NK Header format: %08X, Block: %0X. %s\n",
+ nk_id, (int)nk_hdr, parent->handle->location));
+ return NULL;
+ }
+
+ SMB_REG_ASSERT(size < 0);
+
+ name_len = SVAL(&nk_hdr->nam_len,0);
+ clsname_len = SVAL(&nk_hdr->clsnam_len,0);
+
+ /*
+ * The value of -size should be ge
+ * (sizeof(NK_HDR) - 1 + name_len)
+ * The -1 accounts for the fact that we included the first byte of
+ * the name in the structure. clsname_len is the length of the thing
+ * pointed to by clsnam_off
+ */
+
+ if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
+ DEBUG(0, ("Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr));
+ DEBUG(0, ("Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
+ sizeof(NK_HDR), name_len, clsname_len));
+ /*return NULL;*/
+ }
+
+ DEBUG(2, ("NK HDR: Name len: %d, class name len: %d\n", name_len, clsname_len));
+
+ /* Fish out the key name and process the LF list */
+
+ SMB_REG_ASSERT(name_len < sizeof(key_name));
+
+ strncpy(key_name, nk_hdr->key_nam, name_len);
+ key_name[name_len] = '\0';
+
+ type = (SVAL(&nk_hdr->type,0)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
+ if(type == REG_ROOT_KEY && parent) {
+ DEBUG(0,("Root key encountered below root level!\n"));
+ return NULL;
+ }
+
+ if(type == REG_ROOT_KEY) tmp = reg_key_new_abs(key_name, h, nk_hdr);
+ else tmp = reg_key_new_rel(key_name, parent, nk_hdr);
+
+ DEBUG(2, ("Key name: %s\n", key_name));
+
+ /*
+ * Fish out the class name, it is in UNICODE, while the key name is
+ * ASCII :-)
+ */
+
+ if (clsname_len) { /* Just print in Ascii for now */
+ smb_ucs2_t *clsnamep;
+ int clsnam_off;
+
+ clsnam_off = IVAL(&nk_hdr->clsnam_off,0);
+ clsnamep = (smb_ucs2_t *)LOCN(regf->base, clsnam_off);
+ DEBUG(2, ("Class Name Offset: %0X\n", clsnam_off));
+
+ tmp->class_name = talloc_strdup_w(regf->mem_ctx, clsnamep);
+
+ DEBUGADD(2,(" Class Name: %s\n", cls_name));
+
+ }
+
+ /*
+ * Process the owner offset ...
+ */
+
+ own_off = IVAL(&nk_hdr->own_off,0);
+ own = (REG_KEY *)LOCN(regf->base, own_off);
+ DEBUG(2, ("Owner Offset: %0X\n", own_off));
+
+ DEBUGADD(2, (" Owner locn: %0X, Our locn: %0X\n",
+ (unsigned int)own, (unsigned int)nk_hdr));
+
+ /*
+ * We should verify that the owner field is correct ...
+ * for now, we don't worry ...
+ */
+
+ /*
+ * Also handle the SK header ...
+ */
+
+ sk_off = IVAL(&nk_hdr->sk_off,0);
+ sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
+ DEBUG(2, ("SK Offset: %0X\n", sk_off));
+
+ if (sk_off != -1) {
+
+#if 0
+ tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
+#endif
+
+ }
+
+ return tmp;
+}
+
+/*
+ * Allocate a new hbin block, set up the header for the block etc
+ */
+static HBIN_BLK *nt_create_hbin_blk(REG_HANDLE *h, int size)
+{
+ REGF *regf = h->backend_data;
+ HBIN_BLK *tmp;
+ HBIN_HDR *hdr;
+
+ if (!regf || !size) return NULL;
+
+ /* Round size up to multiple of REGF_HDR_BLKSIZ */
+
+ size = (size + (REGF_HDR_BLKSIZ - 1)) & ~(REGF_HDR_BLKSIZ - 1);
+
+ tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
+ memset(tmp, 0, sizeof(HBIN_BLK));
+
+ tmp->data = malloc(size);
+
+ memset(tmp->data, 0, size); /* Make it pristine */
+
+ tmp->size = size;
+ /*FIXMEtmp->file_offset = regf->blk_tail->file_offset + regf->blk_tail->size;*/
+
+ tmp->free_space = size - (sizeof(HBIN_HDR) - sizeof(HBIN_SUB_HDR));
+ tmp->fsp_off = size - tmp->free_space;
+
+ /*
+ * Now, build the header in the data block
+ */
+ hdr = (HBIN_HDR *)tmp->data;
+ hdr->HBIN_ID = str_to_dword("hbin");
+ hdr->off_from_first = tmp->file_offset - REGF_HDR_BLKSIZ;
+ hdr->off_to_next = tmp->size;
+ hdr->blk_size = tmp->size;
+
+ /*
+ * Now link it in
+ */
+
+ regf->blk_tail->next = tmp;
+ regf->blk_tail = tmp;
+ if (!regf->free_space) regf->free_space = tmp;
+
+ return tmp;
+}
+
+/*
+ * Allocate a unit of space ... and return a pointer as function param
+ * and the block's offset as a side effect
+ */
+static void *nt_alloc_regf_space(REG_HANDLE *h, int size, unsigned int *off)
+{
+ REGF *regf = h->backend_data;
+ int tmp = 0;
+ void *ret = NULL;
+ HBIN_BLK *blk;
+
+ if (!regf || !size || !off) return NULL;
+
+ SMB_REG_ASSERT(regf->blk_head != NULL);
+
+ /*
+ * round up size to include header and then to 8-byte boundary
+ */
+ size = (size + 4 + 7) & ~7;
+
+ /*
+ * Check if there is space, if none, grab a block
+ */
+ if (!regf->free_space) {
+ if (!nt_create_hbin_blk(h, REGF_HDR_BLKSIZ))
+ return NULL;
+ }
+
+ /*
+ * Now, chain down the list of blocks looking for free space
+ */
+
+ for (blk = regf->free_space; blk != NULL; blk = blk->next) {
+ if (blk->free_space <= size) {
+ tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
+ ret = blk->data + blk->fsp_off;
+ blk->free_space -= size;
+ blk->fsp_off += size;
+
+ /* Insert the header */
+ ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
+
+ /*
+ * Fix up the free space ptr
+ * If it is NULL, we fix it up next time
+ */
+
+ if (!blk->free_space)
+ regf->free_space = blk->next;
+
+ *off = tmp;
+ return (((char *)ret)+4);/* The pointer needs to be to the data struct */
+ }
+ }
+
+ /*
+ * If we got here, we need to add another block, which might be
+ * larger than one block -- deal with that later
+ */
+ if (nt_create_hbin_blk(h, REGF_HDR_BLKSIZ)) {
+ blk = regf->free_space;
+ tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ;
+ ret = blk->data + blk->fsp_off;
+ blk->free_space -= size;
+ blk->fsp_off += size;
+
+ /* Insert the header */
+ ((HBIN_SUB_HDR *)ret)->dblocksize = -size;
+
+ /*
+ * Fix up the free space ptr
+ * If it is NULL, we fix it up next time
+ */
+
+ if (!blk->free_space)
+ regf->free_space = blk->next;
+
+ *off = tmp;
+ return (((char *)ret) + 4);/* The pointer needs to be to the data struct */
+ }
+
+ return NULL;
+}
+
+/*
+ * Store a SID at the location provided
+ */
+static int nt_store_SID(REG_HANDLE *regf, DOM_SID *sid, unsigned char *locn)
+{
+ int i;
+ unsigned char *p = locn;
+
+ if (!regf || !sid || !locn) return 0;
+
+ *p = sid->sid_rev_num; p++;
+ *p = sid->num_auths; p++;
+
+ for (i=0; i < 6; i++) {
+ *p = sid->id_auth[i]; p++;
+ }
+
+ for (i=0; i < sid->num_auths; i++) {
+ SIVAL(p, 0, sid->sub_auths[i]); p+=4;
+ }
+
+ return p - locn;
+
+}
+
+static int nt_store_ace(REG_HANDLE *regf, SEC_ACE *ace, unsigned char *locn)
+{
+ int size = 0;
+ SEC_ACE *reg_ace = (SEC_ACE *)locn;
+ unsigned char *p;
+
+ if (!regf || !ace || !locn) return 0;
+
+ reg_ace->type = ace->type;
+ reg_ace->flags = ace->flags;
+
+ /* Deal with the length when we have stored the SID */
+
+ p = (unsigned char *)&reg_ace->info.mask;
+
+ SIVAL(p, 0, ace->info.mask); p += 4;
+
+ size = nt_store_SID(regf, &ace->trustee, p);
+
+ size += 8; /* Size of the fixed header */
+
+ p = (unsigned char *)&reg_ace->size;
+
+ SSVAL(p, 0, size);
+
+ return size;
+}
+
+/*
+ * Store an ACL at the location provided
+ */
+static int nt_store_acl(REG_HANDLE *regf, SEC_ACL *acl, unsigned char *locn) {
+ int size = 0, i;
+ unsigned char *p = locn, *s;
+
+ if (!regf || !acl || !locn) return 0;
+
+ /*
+ * Now store the header and then the ACEs ...
+ */
+
+ SSVAL(p, 0, acl->revision);
+
+ p += 2; s = p; /* Save this for the size field */
+
+ p += 2;
+
+ SIVAL(p, 0, acl->num_aces);
+
+ p += 4;
+
+ for (i = 0; i < acl->num_aces; i++) {
+ size = nt_store_ace(regf, &acl->ace[i], p);
+ p += size;
+ }
+
+ size = s - locn;
+ SSVAL(s, 0, size);
+ return size;
+}
+
+/*
+ * Flatten and store the Sec Desc
+ * Windows lays out the DACL first, but since there is no SACL, it might be
+ * that first, then the owner, then the group SID. So, we do it that way
+ * too.
+ */
+static unsigned int nt_store_sec_desc(REG_HANDLE *regf, SEC_DESC *sd, char *locn)
+{
+ SEC_DESC *rsd = (SEC_DESC *)locn;
+ unsigned int size = 0, off = 0;
+
+ if (!regf || !sd || !locn) return 0;
+
+ /*
+ * Now, fill in the first two fields, then lay out the various fields
+ * as needed
+ */
+
+ rsd->revision = SEC_DESC_REVISION;
+ rsd->type = SEC_DESC_DACL_PRESENT | SEC_DESC_SELF_RELATIVE;
+
+ off = 4 * sizeof(DWORD) + 4;
+
+ if (sd->sacl){
+ size = nt_store_acl(regf, sd->sacl, (char *)(locn + off));
+ rsd->off_sacl = off;
+ }
+ else
+ rsd->off_sacl = 0;
+
+ off += size;
+
+ if (sd->dacl) {
+ rsd->off_dacl = off;
+ size = nt_store_acl(regf, sd->dacl, (char *)(locn + off));
+ }
+ else {
+ rsd->off_dacl = 0;
+ }
+
+ off += size;
+
+ /* Now the owner and group SIDs */
+
+ if (sd->owner_sid) {
+ rsd->off_owner_sid = off;
+ size = nt_store_SID(regf, sd->owner_sid, (char *)(locn + off));
+ }
+ else {
+ rsd->off_owner_sid = 0;
+ }
+
+ off += size;
+
+ if (sd->grp_sid) {
+ rsd->off_grp_sid = off;
+ size = nt_store_SID(regf, sd->grp_sid, (char *)(locn + off));
+ }
+ else {
+ rsd->off_grp_sid = 0;
+ }
+
+ off += size;
+
+ return size;
+}
+
+/*
+ * Store the security information
+ *
+ * If it has already been stored, just get its offset from record
+ * otherwise, store it and record its offset
+ */
+static unsigned int nt_store_security(REG_HANDLE *regf, KEY_SEC_DESC *sec)
+{
+ int size = 0;
+ unsigned int sk_off;
+ SK_HDR *sk_hdr;
+
+ if (sec->offset) return sec->offset;
+
+ /*
+ * OK, we don't have this one in the file yet. We must compute the
+ * size taken by the security descriptor as a self-relative SD, which
+ * means making one pass over each structure and figuring it out
+ */
+
+//FIXME size = sec_desc_size(sec->sec_desc);
+
+ /* Allocate that much space */
+
+ sk_hdr = nt_alloc_regf_space(regf, size, &sk_off);
+ sec->sk_hdr = sk_hdr;
+
+ if (!sk_hdr) return 0;
+
+ /* Now, lay out the sec_desc in the space provided */
+
+ sk_hdr->SK_ID = str_to_dword("sk");
+
+ /*
+ * We can't deal with the next and prev offset in the SK_HDRs until the
+ * whole tree has been stored, then we can go and deal with them
+ */
+
+ sk_hdr->ref_cnt = sec->ref_cnt;
+ sk_hdr->rec_size = size; /* Is this correct */
+
+ /* Now, lay out the sec_desc */
+
+ if (!nt_store_sec_desc(regf, sec->sec_desc, (char *)&sk_hdr->sec_desc))
+ return 0;
+
+ return sk_off;
+
+}
+
+#if 0
+
+/*
+ * Store a KEY in the file ...
+ *
+ * We store this depth first, and defer storing the lf struct until
+ * all the sub-keys have been stored.
+ *
+ * We store the NK hdr, any SK header, class name, and VK structure, then
+ * recurse down the LF structures ...
+ *
+ * We return the offset of the NK struct
+ * FIXME, FIXME, FIXME: Convert to using SIVAL and SSVAL ...
+ */
+static int nt_store_reg_key(REG_HANDLE *regf, REG_KEY *key)
+{
+ NK_HDR *nk_hdr;
+ unsigned int nk_off, sk_off, size;
+
+ if (!regf || !key) return 0;
+
+ size = sizeof(NK_HDR) + strlen(key->name) - 1;
+ nk_hdr = nt_alloc_regf_space(regf, size, &nk_off);
+ if (!nk_hdr) goto error;
+
+ key->offset = nk_off; /* We will need this later */
+
+ /*
+ * Now fill in each field etc ...
+ */
+
+ nk_hdr->NK_ID = str_to_dword("nk");
+ if (key->type == REG_ROOT_KEY)
+ nk_hdr->type = 0x2C;
+ else
+ nk_hdr->type = 0x20;
+
+ /* FIXME: Fill in the time of last update */
+
+ if (key->type != REG_ROOT_KEY)
+ nk_hdr->own_off = key->owner->offset;
+
+ if (key->sub_keys)
+ nk_hdr->subk_num = key->sub_keys->key_count;
+
+ /*
+ * Now, process the Sec Desc and then store its offset
+ */
+
+ sk_off = nt_store_security(regf, key->security);
+ nk_hdr->sk_off = sk_off;
+
+ /*
+ * Then, store the val list and store its offset
+ */
+ if (key->values) {
+ nk_hdr->val_cnt = key->values->val_count;
+ nk_hdr->val_off = nt_store_val_list(regf, key->values);
+ }
+ else {
+ nk_hdr->val_off = -1;
+ nk_hdr->val_cnt = 0;
+ }
+
+ /*
+ * Finally, store the subkeys, and their offsets
+ */
+
+error:
+ return 0;
+}
+#endif
+
+/*
+ * Store the registry header ...
+ * We actually create the registry header block and link it to the chain
+ * of output blocks.
+ */
+static REGF_HDR *nt_get_reg_header(REG_HANDLE *h) {
+ REGF *regf = h->backend_data;
+ HBIN_BLK *tmp = NULL;
+
+ tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK));
+
+ memset(tmp, 0, sizeof(HBIN_BLK));
+ tmp->type = REG_OUTBLK_HDR;
+ tmp->size = REGF_HDR_BLKSIZ;
+ tmp->data = malloc(REGF_HDR_BLKSIZ);
+ if (!tmp->data) goto error;
+
+ memset(tmp->data, 0, REGF_HDR_BLKSIZ); /* Make it pristine, unlike Windows */
+ regf->blk_head = regf->blk_tail = tmp;
+
+ return (REGF_HDR *)tmp->data;
+
+error:
+ if (tmp) free(tmp);
+ return NULL;
+}
+
+static BOOL nt_close_registry (REG_HANDLE *h)
+{
+ REGF *regf = h->backend_data;
+ if (regf->base) munmap(regf->base, regf->sbuf.st_size);
+ regf->base = NULL;
+ close(regf->fd); /* Ignore the error :-) */
+
+ free(regf->sk_map);
+ regf->sk_count = regf->sk_map_size = 0;
+
+ free(regf);
+ return False;
+}
+
+static BOOL nt_open_registry (REG_HANDLE *h, const char *location, BOOL try_load)
+{
+ REGF *regf = (REGF *)malloc(sizeof(REGF));
+ REGF_HDR *regf_hdr;
+ unsigned int regf_id, hbin_id;
+ HBIN_HDR *hbin_hdr;
+
+ memset(regf, 0, sizeof(REGF));
+ regf->mem_ctx = talloc_init("regf");
+ regf->owner_sid_str = def_owner_sid_str;
+ h->backend_data = regf;
+
+ DEBUG(5, ("Attempting to load registry file\n"));
+
+ /* Get the header */
+
+ if ((regf_hdr = nt_get_regf_hdr(h)) == NULL) {
+ DEBUG(0, ("Unable to get header\n"));
+ return False;
+ }
+
+ /* Now process that header and start to read the rest in */
+
+ if ((regf_id = IVAL(&regf_hdr->REGF_ID,0)) != str_to_dword("regf")) {
+ DEBUG(0, ("Unrecognized NT registry header id: %0X, %s\n",
+ regf_id, h->location));
+ return False;
+ }
+
+ /*
+ * Validate the header ...
+ */
+ if (!valid_regf_hdr(regf_hdr)) {
+ DEBUG(0, ("Registry file header does not validate: %s\n",
+ h->location));
+ return False;
+ }
+
+ /* Update the last mod date, and then go get the first NK record and on */
+
+ TTTONTTIME(regf, IVAL(&regf_hdr->tim1,0), IVAL(&regf_hdr->tim2,0));
+
+ /*
+ * The hbin hdr seems to be just uninteresting garbage. Check that
+ * it is there, but that is all.
+ */
+
+ hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
+
+ if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID,0)) != str_to_dword("hbin")) {
+ DEBUG(0, ("Unrecognized registry hbin hdr ID: %0X, %s\n",
+ hbin_id, h->location));
+ return False;
+ }
+
+ /*
+ * Get a pointer to the first key from the hreg_hdr
+ */
+
+ DEBUG(2, ("First Key: %0X\n",
+ IVAL(&regf_hdr->first_key, 0)));
+
+ regf->first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key,0));
+ DEBUGADD(2, ("First Key Offset: %0X\n",
+ IVAL(&regf_hdr->first_key, 0)));
+
+ DEBUGADD(2, ("Data Block Size: %d\n",
+ IVAL(&regf_hdr->dblk_size, 0)));
+
+ DEBUGADD(2, ("Offset to next hbin block: %0X\n",
+ IVAL(&hbin_hdr->off_to_next, 0)));
+
+ DEBUGADD(2, ("HBIN block size: %0X\n",
+ IVAL(&hbin_hdr->blk_size, 0)));
+
+ /*
+ * Unmap the registry file, as we might want to read in another
+ * tree etc.
+ */
+
+ h->backend_data = regf;
+
+ return True;
+}
+
+static REG_KEY *nt_get_root_key(REG_HANDLE *h)
+{
+ return nk_to_key(h, ((REGF *)h->backend_data)->first_key, BLK_SIZE(((REGF *)h->backend_data)->first_key), NULL);
+}
+
+static int nt_num_subkeys(REG_KEY *k)
+{
+ REGF *regf = k->handle->backend_data;
+ LF_HDR *lf_hdr;
+ int lf_off;
+ NK_HDR *nk_hdr = k->backend_data;
+ lf_off = IVAL(&nk_hdr->lf_off,0);
+ DEBUG(2, ("SubKey list offset: %0X\n", lf_off));
+ if(lf_off == -1) return 0;
+ lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
+
+ return lf_num_entries(k->handle, lf_hdr, BLK_SIZE(lf_hdr));
+}
+
+static int nt_num_values(REG_KEY *k)
+{
+ NK_HDR *nk_hdr = k->backend_data;
+ return IVAL(&nk_hdr->val_cnt,0);
+}
+
+static REG_VAL *nt_value_by_index(REG_KEY *k, int n)
+{
+ VL_TYPE *vl;
+ int val_off, vk_off;
+ VK_HDR *vk_hdr;
+ REGF *regf = k->handle->backend_data;
+ NK_HDR *nk_hdr = k->backend_data;
+ val_off = IVAL(&nk_hdr->val_off,0);
+ vl = (VL_TYPE *)LOCN(regf->base, val_off);
+ DEBUG(2, ("Val List Offset: %0X\n", val_off));
+
+ vk_off = IVAL(&vl[n],0);
+ vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
+ return vk_to_val(k, vk_hdr, BLK_SIZE(vk_hdr));
+}
+
+static REG_KEY *nt_key_by_index(REG_KEY *k, int n)
+{
+ REGF *regf = k->handle->backend_data;
+ int lf_off;
+ NK_HDR *nk_hdr = k->backend_data;
+ LF_HDR *lf_hdr;
+ lf_off = IVAL(&nk_hdr->lf_off,0);
+ DEBUG(2, ("SubKey list offset: %0X\n", lf_off));
+
+ /*
+ * No more subkeys if lf_off == -1
+ */
+
+ if (lf_off != -1) {
+ lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
+ return lf_get_entry(k, lf_hdr, BLK_SIZE(lf_hdr), n);
+ }
+
+ return NULL;
+}
+
+static REG_OPS reg_backend_nt4 = {
+ .name = "nt4",
+ .open_registry = nt_open_registry,
+ .close_registry = nt_close_registry,
+ .open_root_key = nt_get_root_key,
+ .num_subkeys = nt_num_subkeys,
+ .num_values = nt_num_values,
+ .get_subkey_by_index = nt_key_by_index,
+ .get_value_by_index = nt_value_by_index,
+
+ /* TODO:
+ .add_key
+ .add_value
+ .del_key
+ .del_value
+ .update_value
+ */
+};
+
+NTSTATUS reg_nt4_init(void)
+{
+ return register_backend("registry", &reg_backend_nt4);
+}
diff --git a/source4/lib/registry/reg_backend_rpc/reg_backend_rpc.c b/source4/lib/registry/reg_backend_rpc/reg_backend_rpc.c
new file mode 100644
index 0000000000..7e8ad9bf48
--- /dev/null
+++ b/source4/lib/registry/reg_backend_rpc/reg_backend_rpc.c
@@ -0,0 +1,311 @@
+/*
+ Samba Unix/Linux SMB implementation
+ RPC backend for the registry library
+ Copyright (C) 2003-2004 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/registry/common/registry.h"
+
+static void init_winreg_String(struct winreg_String *name, const char *s)
+{
+ name->name = s;
+ if (s) {
+ name->name_len = 2 * (strlen_m(s) + 1);
+ name->name_size = name->name_len;
+ } else {
+ name->name_len = 0;
+ name->name_size = 0;
+ }
+}
+
+
+#define openhive(u) static struct policy_handle *open_ ## u(struct dcerpc_pipe *p, REG_HANDLE *h) \
+{ \
+ NTSTATUS status; \
+ struct winreg_Open ## u r; \
+ struct winreg_OpenUnknown unknown; \
+ struct policy_handle *hnd = malloc(sizeof(struct policy_handle)); \
+ TALLOC_CTX *mem_ctx = talloc_init("openhive"); \
+ \
+ unknown.unknown0 = 0x84e0; \
+ unknown.unknown1 = 0x0000; \
+ r.in.unknown = &unknown; \
+ r.in.access_required = SEC_RIGHTS_MAXIMUM_ALLOWED; \
+ r.out.handle = hnd;\
+ \
+ if (!NT_STATUS_IS_OK(dcerpc_winreg_Open ## u(p, mem_ctx, &r))) {\
+ printf("Error executing open\n");\
+ return NULL;\
+ }\
+\
+ talloc_destroy(mem_ctx);\
+\
+ return hnd;\
+}
+
+openhive(HKLM)
+openhive(HKCU)
+openhive(HKPD)
+openhive(HKU)
+openhive(HKCR)
+
+struct rpc_data {
+ struct dcerpc_pipe *pipe;
+ struct policy_handle *hives[10];
+};
+
+struct {
+ char *name;
+ struct policy_handle *(*open) (struct dcerpc_pipe *p, REG_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 },
+{ NULL, NULL }
+};
+
+static BOOL rpc_open_registry(REG_HANDLE *h, const char *location, BOOL try_full)
+{
+ BOOL res = True;
+ struct rpc_data *mydata = malloc(sizeof(struct rpc_data));
+ char *binding = strdup(location);
+ NTSTATUS status;
+
+ ZERO_STRUCTP(mydata);
+
+ status = dcerpc_pipe_connect(&mydata->pipe, binding,
+ DCERPC_WINREG_UUID,
+ DCERPC_WINREG_VERSION,
+ lp_workgroup(),
+ "jelwin", "dds");
+
+ if(!NT_STATUS_IS_OK(status)) return False;
+
+ h->backend_data = mydata;
+
+ return True;
+}
+
+static REG_KEY *rpc_open_root(REG_HANDLE *h)
+{
+ /* There's not really a 'root' key here */
+ return reg_key_new_abs("\\", h, h->backend_data);
+}
+
+static BOOL rpc_close_registry(REG_HANDLE *h)
+{
+ dcerpc_pipe_close(((struct rpc_data *)h->backend_data)->pipe);
+ free(h->backend_data);
+ return True;
+}
+
+static struct policy_handle *rpc_get_key_handle(REG_HANDLE *h, const char *path)
+{
+ char *hivename;
+ int i = 0;
+ struct rpc_data *mydata = h->backend_data;
+ struct policy_handle *hive = NULL;
+ char *end = strchr(path+1, '\\');
+ NTSTATUS status;
+ struct winreg_OpenKey r;
+ struct policy_handle *key_handle = malloc(sizeof(struct policy_handle));
+ TALLOC_CTX *mem_ctx;
+
+ if(end) hivename = strndup(path+1, end-path-1);
+ else hivename = strdup(path+1);
+
+ for(i = 0; known_hives[i].name; i++) {
+ if(!strcmp(hivename, known_hives[i].name)) {
+ if(!mydata->hives[i]) mydata->hives[i] = known_hives[i].open(mydata->pipe, h);
+ hive = mydata->hives[i];
+ }
+ }
+
+ if(!hive) {
+ DEBUG(0, ("No such hive: %s\n", hivename));
+ return NULL;
+ }
+
+ DEBUG(2, ("Opening %s, hive: %s\n", path, hivename));
+
+ if(!end || !(*end) || !(*(end+1))) return hive;
+
+ memset(&r, 0, sizeof(struct winreg_OpenKey));
+ r.in.handle = hive;
+ init_winreg_String(&r.in.keyname, end+1);
+ r.in.unknown = 0x00000000;
+ r.in.access_mask = 0x02000000;
+ r.out.handle = key_handle;
+
+ mem_ctx = talloc_init("openkey");
+ status = dcerpc_winreg_OpenKey(mydata->pipe, mem_ctx, &r);
+ talloc_destroy(mem_ctx);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
+ return NULL;
+ }
+
+ return key_handle;
+}
+
+static REG_KEY *rpc_open_key(REG_HANDLE *h, const char *name)
+{
+ return reg_key_new_abs(name, h, rpc_get_key_handle(h, name));
+}
+
+static BOOL rpc_fetch_subkeys(REG_KEY *parent, int *count, REG_KEY ***subkeys)
+{
+ struct winreg_EnumKey r;
+ struct winreg_EnumKeyNameRequest keyname;
+ struct winreg_String classname;
+ struct winreg_Time tm;
+ struct rpc_data *mydata = parent->handle->backend_data;
+ int i;
+ REG_KEY **ar = malloc(sizeof(REG_KEY *));
+ NTSTATUS status = NT_STATUS_OK;
+ TALLOC_CTX *mem_ctx;
+
+ /* List the hives */
+ if(parent->backend_data == parent->handle->backend_data) {
+ REG_KEY **ar = malloc(sizeof(REG_KEY *));
+ for(i = 0; known_hives[i].name; i++) {
+ ar[i] = reg_key_new_rel(known_hives[i].name, parent, NULL);
+ (*count)++;
+ ar = realloc(ar, sizeof(REG_KEY *) * ((*count)+1));
+ }
+
+ *subkeys = ar;
+
+ return True;
+ }
+
+ if(!parent->backend_data) parent->backend_data = rpc_get_key_handle(parent->handle, reg_key_get_path(parent));
+
+ if(!parent->backend_data) return False;
+
+ mem_ctx = talloc_init("enumkey");
+ (*count) = 0;
+ r.in.handle = parent->backend_data;
+ keyname.unknown = 0x0000020a;
+ init_winreg_String(&keyname.key_name, NULL);
+ init_winreg_String(&classname, NULL);
+ r.in.in_name = &keyname;
+ r.in.class = &classname;
+ tm.low = tm.high = 0x7fffffff;
+ r.in.last_changed_time = &tm;
+ r.out.result.v = 0;
+
+ for(i = 0; NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result); i++) {
+ r.in.enum_index = i;
+ r.in.unknown = r.out.unknown = 0x0414;
+ r.in.key_name_len = r.out.key_name_len = 0;
+ status = dcerpc_winreg_EnumKey(mydata->pipe, mem_ctx, &r);
+ if(NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)) {
+ ar[(*count)] = reg_key_new_rel(r.out.out_name->name, parent, NULL);
+ (*count)++;
+ ar = realloc(ar, ((*count)+1) * sizeof(REG_KEY *));
+ }
+ }
+
+ *subkeys = ar;
+ return True;
+}
+
+static BOOL rpc_fetch_values(REG_KEY *parent, int *count, REG_VAL ***values)
+{
+ struct winreg_EnumValue r;
+ struct winreg_EnumValueName name;
+ struct winreg_Uint8buf value;
+ struct winreg_Uint16buf buf;
+ struct rpc_data *mydata = parent->handle->backend_data;
+ TALLOC_CTX *mem_ctx;
+ uint32 type, requested_len, returned_len;
+ NTSTATUS status = NT_STATUS_OK;
+ REG_VAL **ar = malloc(sizeof(REG_VAL *));
+
+ (*count) = 0;
+
+ if(!parent->backend_data) parent->backend_data = rpc_get_key_handle(parent->handle, reg_key_get_path(parent));
+
+ if(!parent->backend_data) return False;
+
+ r.in.handle = parent->backend_data;
+ r.in.enum_index = 0;
+
+ buf.max_len = 0x7fff;
+ buf.offset = 0;
+ buf.len = 0;
+ buf.buffer = NULL;
+
+ name.len = 0;
+ name.max_len = buf.max_len *2;
+ name.buf = &buf;
+
+ r.in.name = r.out.name = &name;
+
+ type = 0;
+ r.in.type = r.out.type = &type;
+ value.max_len = 0x7fff;
+ value.offset = 0;
+ value.len = 0;
+ value.buffer = NULL;
+
+ r.in.value = r.out.value = &value;
+
+ requested_len = value.max_len;
+ r.in.requested_len = &requested_len;
+ returned_len = 0;
+ r.in.returned_len = &returned_len;
+ r.out.result.v = 0;
+
+ mem_ctx = talloc_init("fetchvalues");
+ while(1) {
+ status = dcerpc_winreg_EnumValue(mydata->pipe, mem_ctx, &r);
+ if(NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)) {
+ r.in.enum_index++;
+ ar[(*count)] = reg_val_new(parent, NULL);
+ ar[(*count)]->name = strdup((char *)name.buf);
+ ar[(*count)]->data_type = *r.out.type;
+ ar[(*count)]->data_len = value.len;
+ ar[(*count)]->data_blk = malloc(value.len);
+ memcpy(ar[(*count)]->data_blk, value.buffer, value.len);
+ (*count)++;
+ ar = realloc(ar, ((*count)+1) * sizeof(REG_VAL *));
+ } else break;
+ }
+
+ talloc_destroy(mem_ctx);
+
+ return True;
+}
+
+static REG_OPS reg_backend_rpc = {
+ .name = "rpc",
+ .open_registry = rpc_open_registry,
+ .close_registry = rpc_close_registry,
+ .open_root_key = rpc_open_root,
+ .open_key = rpc_open_key,
+ .fetch_subkeys = rpc_fetch_subkeys,
+ .fetch_values = rpc_fetch_values,
+};
+
+NTSTATUS reg_rpc_init(void)
+{
+ return register_backend("registry", &reg_backend_rpc);
+}
diff --git a/source4/lib/registry/reg_backend_wine/reg_backend_wine.c b/source4/lib/registry/reg_backend_wine/reg_backend_wine.c
new file mode 100644
index 0000000000..6c8d7885cb
--- /dev/null
+++ b/source4/lib/registry/reg_backend_wine/reg_backend_wine.c
@@ -0,0 +1,32 @@
+/*
+ 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 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/common/registry.h"
+
+static REG_OPS reg_backend_wine = {
+ .name = "wine",
+};
+
+NTSTATUS reg_wine_init(void)
+{
+ register_backend("registry", &reg_backend_wine);
+ return NT_STATUS_OK;
+}
diff --git a/source4/lib/registry/tools/regdiff.c b/source4/lib/registry/tools/regdiff.c
new file mode 100644
index 0000000000..070516b798
--- /dev/null
+++ b/source4/lib/registry/tools/regdiff.c
@@ -0,0 +1,148 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ 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 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"
+
+void writediff(REG_KEY *oldkey, REG_KEY *newkey, FILE *out)
+{
+ int i, numvals1, numvals2, numkeys2;
+
+ numkeys2 = reg_key_num_subkeys(newkey);
+ for(i = 0; i < numkeys2; i++) {
+ REG_KEY *t1 = reg_key_get_subkey_by_index(newkey, i);
+ REG_KEY *t2 = reg_key_get_subkey_by_name(oldkey, reg_key_name(t1));
+ if(!t2) {
+ fprintf(out, "[%s]\n", reg_key_get_path(t1));
+ }
+ writediff(t2, t1, out);
+ }
+
+ numvals2 = reg_key_num_values(newkey);
+ for(i = 0; i < numvals2; i++) {
+ REG_VAL *t1 = reg_key_get_value_by_index(newkey, i);
+ REG_VAL *t2 = reg_key_get_value_by_name(oldkey, reg_val_name(t1));
+ if(!t2 || reg_val_size(t2) != reg_val_size(t1) || memcmp(reg_val_data_blk(t1), reg_val_data_blk(t2), reg_val_size(t1))) {
+ fprintf(out, "\"%s\"=%s:%s\n", reg_val_name(t1), str_regtype(reg_val_type(t1)), reg_val_data_string(t1));
+ }
+ }
+
+ numvals1 = reg_key_num_values(oldkey);
+ for(i = 0; i < numvals1; i++) {
+ REG_VAL *t1 = reg_key_get_value_by_index(oldkey, i);
+ if(!reg_key_get_value_by_name(newkey, reg_val_name(t1))) {
+ fprintf(out, "\"%s\"=-\n", reg_val_name(t1));
+ }
+ }
+}
+
+int main (int argc, char **argv)
+{
+ uint32 setparms, checkparms;
+ int opt;
+ poptContext pc;
+ REG_KEY *root;
+ const char *backend1 = NULL, *backend2 = NULL;
+ const char *location2;
+ char *outputfile = NULL;
+ FILE *fd = stdout;
+ REG_HANDLE *h2;
+ REG_KEY *root1 = NULL, *root2;
+ int from_null = 0;
+ int fullpath = 0, no_values = 0;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"backend", 'b', POPT_ARG_STRING, NULL, 'b', "backend to use", NULL},
+ {"output", 'o', POPT_ARG_STRING, &outputfile, 'o', "output file to use", NULL },
+ {"null", 'n', POPT_ARG_NONE, &from_null, 'n', "Diff from NULL" },
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ case 'b':
+ if(!backend1 && !from_null) backend1 = poptGetOptArg(pc);
+ else if(!backend2) backend2 = poptGetOptArg(pc);
+ break;
+ }
+ }
+ setup_logging(argv[0], True);
+
+ if(!from_null) {
+ REG_HANDLE *h1;
+ const char *location1;
+ location1 = poptGetArg(pc);
+ if(!location1) {
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+
+ if(!backend1) backend1 = "dir";
+
+ h1 = reg_open(backend1, location1, True);
+ if(!h1) {
+ fprintf(stderr, "Unable to open '%s' with backend '%s'\n", location1, backend1);
+ return 1;
+ }
+
+ root1 = reg_get_root(h1);
+ }
+
+ location2 = poptGetArg(pc);
+ if(!location2) {
+ poptPrintUsage(pc, stderr, 0);
+ return 2;
+ }
+
+ if(!backend2) backend2 = "dir";
+
+ h2 = reg_open(backend2, location2, True);
+ if(!h2) {
+ fprintf(stderr, "Unable to open '%s' with backend '%s'\n", location2, backend2);
+ return 1;
+ }
+
+ root2 = reg_get_root(h2);
+ if(!root2) {
+ fprintf(stderr, "Can't open root key for '%s:%s'\n", backend2, location2);
+ return 1;
+ }
+
+ poptFreeContext(pc);
+
+ if(outputfile) {
+ fd = fopen(outputfile, "w+");
+ if(!fd) {
+ fprintf(stderr, "Unable to open '%s'\n", outputfile);
+ return 1;
+ }
+ }
+
+ fprintf(fd, "REGEDIT4\n\n");
+ fprintf(fd, "; Generated using regdiff\n");
+
+ writediff(root1, root2, fd);
+
+ fclose(fd);
+
+ return 0;
+}
diff --git a/source4/lib/registry/tools/regpatch.c b/source4/lib/registry/tools/regpatch.c
new file mode 100644
index 0000000000..f76da7ebf9
--- /dev/null
+++ b/source4/lib/registry/tools/regpatch.c
@@ -0,0 +1,808 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ Copyright (C) 2002, Richard Sharpe, rsharpe@richardsharpe.com
+ Copyright (C) 2004, 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"
+
+/*
+ * Routines to parse a REGEDIT4 file
+ *
+ * The file consists of:
+ *
+ * REGEDIT4
+ * \[[-]key-path\]\n
+ * <value-spec>*
+ *
+ * Format:
+ * [cmd:]name=type:value
+ *
+ * cmd = a|d|c|add|delete|change|as|ds|cs
+ *
+ * There can be more than one key-path and value-spec.
+ *
+ * Since we want to support more than one type of file format, we
+ * construct a command-file structure that keeps info about the command file
+ */
+
+#define FMT_UNREC -1
+#define FMT_REGEDIT4 0
+#define FMT_EDITREG1_1 1
+
+#define FMT_STRING_REGEDIT4 "REGEDIT4"
+#define FMT_STRING_EDITREG1_0 "EDITREG1.0"
+
+#define CMD_NONE 0
+#define CMD_ADD_KEY 1
+#define CMD_DEL_KEY 2
+
+#define CMD_KEY 1
+#define CMD_VAL 2
+
+#include <include/includes.h>
+
+typedef struct val_spec_list {
+ struct val_spec_list *next;
+ char *name;
+ int type;
+ char *val; /* Kept as a char string, really? */
+} VAL_SPEC_LIST;
+
+typedef struct command_s {
+ int cmd;
+ char *key;
+ int val_count;
+ VAL_SPEC_LIST *val_spec_list, *val_spec_last;
+} CMD;
+
+typedef struct cmd_line {
+ int len, line_len;
+ char *line;
+} CMD_LINE;
+
+static void free_val_spec_list(VAL_SPEC_LIST *vl)
+{
+ if (!vl) return;
+ if (vl->name) free(vl->name);
+ if (vl->val) free(vl->val);
+ free(vl);
+
+}
+
+/*
+ * Some routines to handle lines of info in the command files
+ */
+static void skip_to_eol(int fd)
+{
+ int rc;
+ char ch = 0;
+
+ while ((rc = read(fd, &ch, 1)) == 1) {
+ if (ch == 0x0A) return;
+ }
+ if (rc < 0) {
+ DEBUG(0, ("Could not read file descriptor: %d, %s\n",
+ fd, strerror(errno)));
+ exit(1);
+ }
+}
+
+static void free_cmd(CMD *cmd)
+{
+ if (!cmd) return;
+
+ while (cmd->val_spec_list) {
+ VAL_SPEC_LIST *tmp;
+
+ tmp = cmd->val_spec_list;
+ cmd->val_spec_list = tmp->next;
+ free(tmp);
+ }
+
+ free(cmd);
+
+}
+
+static void free_cmd_line(CMD_LINE *cmd_line)
+{
+ if (cmd_line) {
+ if (cmd_line->line) free(cmd_line->line);
+ free(cmd_line);
+ }
+}
+
+static void print_line(struct cmd_line *cl)
+{
+ char *pl;
+
+ if (!cl) return;
+
+ pl = smb_xmalloc(cl->line_len + 1);
+
+ strncpy(pl, cl->line, cl->line_len);
+ pl[cl->line_len] = 0;
+
+ fprintf(stdout, "%s\n", pl);
+ free(pl);
+}
+
+#define INIT_ALLOC 10
+
+/*
+ * Read a line from the input file.
+ * NULL returned when EOF and no chars read
+ * Otherwise we return a cmd_line *
+ * Exit if other errors
+ */
+static struct cmd_line *get_cmd_line(int fd)
+{
+ struct cmd_line *cl = (CMD_LINE *)smb_xmalloc(sizeof(CMD_LINE));
+ int i = 0, rc;
+ unsigned char ch;
+
+ cl->len = INIT_ALLOC;
+
+ /*
+ * Allocate some space for the line. We extend later if needed.
+ */
+
+ cl->line = (char *)smb_xmalloc(INIT_ALLOC);
+
+ /*
+ * Now read in the chars to EOL. Don't store the EOL in the
+ * line. What about CR?
+ */
+
+ while ((rc = read(fd, &ch, 1)) == 1 && ch != '\n') {
+ if (ch == '\r') continue; /* skip CR */
+ if (i == cl->len) {
+ /*
+ * Allocate some more memory
+ */
+ if ((cl->line = realloc(cl->line, cl->len + INIT_ALLOC)) == NULL) {
+ DEBUG(0, ("Unable to realloc space for line: %s\n",
+ strerror(errno)));
+ exit(1);
+ }
+ cl->len += INIT_ALLOC;
+ }
+ cl->line[i] = ch;
+ i++;
+ }
+
+ /* read 0 and we were at loc'n 0, return NULL */
+ if (rc == 0 && i == 0) {
+ free_cmd_line(cl);
+ return NULL;
+ }
+
+ cl->line_len = i;
+
+ return cl;
+
+}
+
+/*
+ * parse_value: parse out a value. We pull it apart as:
+ *
+ * <value> ::= <value-name>=<type>:<value-string>
+ *
+ * <value-name> ::= char-string-without-spaces | '"' char-string '"'
+ *
+ * If it parsed OK, return the <value-name> as a string, and the
+ * value type and value-string in parameters.
+ *
+ * The value name can be empty. There can only be one empty name in
+ * a list of values. A value of - removes the value entirely.
+ */
+
+static char *parse_name(char *nstr)
+{
+ int len = 0, start = 0;
+ if (!nstr) return NULL;
+
+ len = strlen(nstr);
+
+ while (len && nstr[len - 1] == ' ') len--;
+
+ nstr[len] = 0; /* Trim any spaces ... if there were none, doesn't matter */
+
+ /*
+ * Beginning and end should be '"' or neither should be so
+ */
+ if ((nstr[0] == '"' && nstr[len - 1] != '"') ||
+ (nstr[0] != '"' && nstr[len - 1] == '"'))
+ return NULL;
+
+ if (nstr[0] == '"') {
+ start = 1;
+ len -= 2;
+ }
+
+ return strndup(&nstr[start], len);
+}
+
+static int parse_value_type(char *tstr)
+{
+ int len = strlen(tstr);
+
+ while (len && tstr[len - 1] == ' ') len--;
+ tstr[len] = 0;
+
+ if (strcmp(tstr, "REG_DWORD") == 0)
+ return REG_DWORD;
+ else if (strcmp(tstr, "dword") == 0)
+ return REG_DWORD;
+ else if (strcmp(tstr, "REG_EXPAND_SZ") == 0)
+ return REG_EXPAND_SZ;
+ else if (strcmp(tstr, "REG_BIN") == 0)
+ return REG_BINARY;
+ else if (strcmp(tstr, "REG_SZ") == 0)
+ return REG_SZ;
+ else if (strcmp(tstr, "REG_MULTI_SZ") == 0)
+ return REG_MULTI_SZ;
+ else if (strcmp(tstr, "-") == 0)
+ return REG_DELETE;
+
+ return 0;
+}
+
+static char *parse_val_str(char *vstr)
+{
+
+ return strndup(vstr, strlen(vstr));
+
+}
+
+static char *parse_value(struct cmd_line *cl, int *vtype, char **val)
+{
+ char *p1 = NULL, *p2 = NULL, *nstr = NULL, *tstr = NULL, *vstr = NULL;
+
+ if (!cl || !vtype || !val) return NULL;
+ if (!cl->line_len) return NULL;
+
+ p1 = strndup(cl->line, cl->line_len);
+ /* FIXME: Better return codes etc ... */
+ if (!p1) return NULL;
+ p2 = strchr(p1, '=');
+ if (!p2) return NULL;
+
+ *p2 = 0; p2++; /* Split into two strings at p2 */
+
+ /* Now, parse the name ... */
+
+ nstr = parse_name(p1);
+ if (!nstr) goto error;
+
+ /* Now, split the remainder and parse on type and val ... */
+
+ tstr = p2;
+ while (*tstr == ' ') tstr++; /* Skip leading white space */
+ p2 = strchr(p2, ':');
+
+ if (p2) {
+ *p2 = 0; p2++; /* split on the : */
+ }
+
+ *vtype = parse_value_type(tstr);
+
+ if (!vtype) goto error;
+
+ if (!p2 || !*p2) return nstr;
+
+ /* Now, parse the value string. It should return a newly malloc'd string */
+
+ while (*p2 == ' ') p2++; /* Skip leading space */
+ vstr = parse_val_str(p2);
+
+ if (!vstr) goto error;
+
+ *val = vstr;
+
+ return nstr;
+
+ error:
+ if (p1) free(p1);
+ if (nstr) free(nstr);
+ if (vstr) free(vstr);
+ return NULL;
+}
+
+/*
+ * Parse out a key. Look for a correctly formatted key [...]
+ * and whether it is a delete or add? A delete is signalled
+ * by a - in front of the key.
+ * Assumes that there are no leading and trailing spaces
+ */
+
+static char *parse_key(struct cmd_line *cl, int *cmd)
+{
+ int start = 1;
+ char *tmp;
+
+ if (cl->line[0] != '[' ||
+ cl->line[cl->line_len - 1] != ']') return NULL;
+ if (cl->line_len == 2) return NULL;
+ *cmd = CMD_ADD_KEY;
+ if (cl->line[1] == '-') {
+ if (cl->line_len == 3) return NULL;
+ start = 2;
+ *cmd = CMD_DEL_KEY;
+ }
+ tmp = smb_xmalloc(cl->line_len - 1 - start + 1);
+ strncpy(tmp, &cl->line[start], cl->line_len - 1 - start);
+ tmp[cl->line_len - 1 - start] = 0;
+ return tmp;
+}
+
+/*
+ * Parse a line to determine if we have a key or a value
+ * We only check for key or val ...
+ */
+
+static int parse_line(struct cmd_line *cl)
+{
+
+ if (!cl || cl->len == 0) return 0;
+
+ if (cl->line[0] == '[') /* No further checking for now */
+ return CMD_KEY;
+ else
+ return CMD_VAL;
+}
+
+/*
+ * We seek to offset 0, read in the required number of bytes,
+ * and compare to the correct value.
+ * We then seek back to the original location
+ */
+static int regedit4_file_type(int fd)
+{
+ int cur_ofs = 0;
+ char desc[9];
+
+ cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
+ if (cur_ofs < 0) {
+ DEBUG(0, ("Unable to get current offset: %s\n", strerror(errno)));
+ exit(1); /* FIXME */
+ }
+
+ if (cur_ofs) {
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ if (read(fd, desc, 8) < 8) {
+ DEBUG(0, ("Unable to read command file format\n"));
+ exit(2); /* FIXME */
+ }
+
+ desc[8] = 0;
+
+ if (strcmp(desc, FMT_STRING_REGEDIT4) == 0) {
+ if (cur_ofs) {
+ lseek(fd, cur_ofs, SEEK_SET);
+ }
+ else {
+ skip_to_eol(fd);
+ }
+ return FMT_REGEDIT4;
+ }
+
+ return FMT_UNREC;
+}
+
+/*
+ * Run though the data in the line and strip anything after a comment
+ * char.
+ */
+static void strip_comment(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = 0; i < cl->line_len; i++) {
+ if (cl->line[i] == ';') {
+ cl->line_len = i;
+ return;
+ }
+ }
+}
+
+/*
+ * trim leading space
+ */
+
+static void trim_leading_spaces(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = 0; i < cl->line_len; i++) {
+ if (cl->line[i] != ' '){
+ if (i) memcpy(cl->line, &cl->line[i], cl->line_len - i);
+ return;
+ }
+ }
+}
+
+/*
+ * trim trailing spaces
+ */
+static void trim_trailing_spaces(struct cmd_line *cl)
+{
+ int i;
+
+ if (!cl) return;
+
+ for (i = cl->line_len; i == 0; i--) {
+ if (cl->line[i-1] != ' ' &&
+ cl->line[i-1] != '\t') {
+ cl->line_len = i;
+ }
+ }
+}
+
+/*
+ * Get a command ... This consists of possibly multiple lines:
+ * [key]
+ * values*
+ * possibly Empty line
+ *
+ * value ::= <value-name>=<value-type>':'<value-string>
+ * <value-name> is some path, possibly enclosed in quotes ...
+ * We alctually look for the next key to terminate a previous key
+ * if <value-type> == '-', then it is a delete type.
+ */
+static CMD *regedit4_get_cmd(int fd)
+{
+ struct command_s *cmd = NULL;
+ struct cmd_line *cl = NULL;
+ struct val_spec_list *vl = NULL;
+
+ cmd = (struct command_s *)smb_xmalloc(sizeof(struct command_s));
+
+ cmd->cmd = CMD_NONE;
+ cmd->key = NULL;
+ cmd->val_count = 0;
+ cmd->val_spec_list = cmd->val_spec_last = NULL;
+ while ((cl = get_cmd_line(fd))) {
+
+ /*
+ * If it is an empty command line, and we already have a key
+ * then exit from here ... FIXME: Clean up the parser
+ */
+
+ if (cl->line_len == 0 && cmd->key) {
+ free_cmd_line(cl);
+ break;
+ }
+
+ strip_comment(cl); /* remove anything beyond a comment char */
+ trim_trailing_spaces(cl);
+ trim_leading_spaces(cl);
+
+ if (cl->line_len == 0) { /* An empty line */
+ free_cmd_line(cl);
+ }
+ else { /* Else, non-empty ... */
+ /*
+ * Parse out the bits ...
+ */
+ switch (parse_line(cl)) {
+ case CMD_KEY:
+ if ((cmd->key = parse_key(cl, &cmd->cmd)) == NULL) {
+ DEBUG(0, ("Error parsing key from line: "));
+ print_line(cl);
+ DEBUG(0, ("\n"));
+ }
+ break;
+
+ case CMD_VAL:
+ /*
+ * We need to add the value stuff to the list
+ * There could be a \ on the end which we need to
+ * handle at some time
+ */
+ vl = (struct val_spec_list *)smb_xmalloc(sizeof(struct val_spec_list));
+ vl->next = NULL;
+ vl->val = NULL;
+ vl->name = parse_value(cl, &vl->type, &vl->val);
+ if (!vl->name) goto error;
+ if (cmd->val_spec_list == NULL) {
+ cmd->val_spec_list = cmd->val_spec_last = vl;
+ }
+ else {
+ cmd->val_spec_last->next = vl;
+ cmd->val_spec_last = vl;
+ }
+ cmd->val_count++;
+ break;
+
+ default:
+ DEBUG(0, ("Unrecognized line in command file: \n"));
+ print_line(cl);
+ break;
+ }
+ }
+
+ }
+ if (!cmd->cmd) goto error; /* End of file ... */
+
+ return cmd;
+
+ error:
+ if (vl) free(vl);
+ if (cmd) free_cmd(cmd);
+ return NULL;
+}
+
+static int regedit4_exec_cmd(CMD *cmd)
+{
+
+ return 0;
+}
+
+static int editreg_1_0_file_type(int fd)
+{
+ int cur_ofs = 0;
+ char desc[11];
+
+ cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */
+ if (cur_ofs < 0) {
+ DEBUG(0, ("Unable to get current offset: %s\n", strerror(errno)));
+ exit(1); /* FIXME */
+ }
+
+ if (cur_ofs) {
+ lseek(fd, 0, SEEK_SET);
+ }
+
+ if (read(fd, desc, 10) < 10) {
+ DEBUG(0, ("Unable to read command file format\n"));
+ exit(2); /* FIXME */
+ }
+
+ desc[10] = 0;
+
+ if (strcmp(desc, FMT_STRING_EDITREG1_0) == 0) {
+ lseek(fd, cur_ofs, SEEK_SET);
+ return FMT_REGEDIT4;
+ }
+
+ return FMT_UNREC;
+}
+
+static CMD *editreg_1_0_get_cmd(int fd)
+{
+ return NULL;
+}
+
+static int editreg_1_0_exec_cmd(CMD *cmd)
+{
+
+ return -1;
+}
+
+typedef struct command_ops_s {
+ int type;
+ int (*file_type)(int fd);
+ CMD *(*get_cmd)(int fd);
+ int (*exec_cmd)(CMD *cmd);
+} CMD_OPS;
+
+CMD_OPS default_cmd_ops[] = {
+ {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd},
+ {1, editreg_1_0_file_type, editreg_1_0_get_cmd, editreg_1_0_exec_cmd},
+ {-1, NULL, NULL, NULL}
+};
+
+typedef struct command_file_s {
+ char *name;
+ int type, fd;
+ CMD_OPS cmd_ops;
+} CMD_FILE;
+
+/*
+ * Create a new command file structure
+ */
+
+static CMD_FILE *cmd_file_create(char *file)
+{
+ CMD_FILE *tmp;
+ struct stat sbuf;
+ int i = 0;
+
+ /*
+ * Let's check if the file exists ...
+ * No use creating the cmd_file structure if the file does not exist
+ */
+
+ if (stat(file, &sbuf) < 0) { /* Not able to access file */
+
+ return NULL;
+ }
+
+ tmp = (CMD_FILE *)smb_xmalloc(sizeof(CMD_FILE));
+
+ /*
+ * Let's fill in some of the fields;
+ */
+
+ tmp->name = strdup(file);
+
+ if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) {
+ free(tmp);
+ return NULL;
+ }
+
+ /*
+ * Now, try to find the format by indexing through the table
+ */
+ while (default_cmd_ops[i].type != -1) {
+ if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) {
+ tmp->cmd_ops = default_cmd_ops[i];
+ return tmp;
+ }
+ i++;
+ }
+
+ /*
+ * If we got here, return NULL, as we could not figure out the type
+ * of command file.
+ *
+ * What about errors?
+ */
+
+ free(tmp);
+ return NULL;
+}
+
+/*
+ * Extract commands from the command file, and execute them.
+ * We pass a table of command callbacks for that
+ */
+
+//FIXME
+
+/*
+ * Main code from here on ...
+ */
+
+/*
+ * key print function here ...
+ */
+
+/*
+ * Sec Desc print functions
+ */
+
+char *str_type(unsigned char type);
+
+int nt_apply_reg_command_file(REG_HANDLE *regf, const char *cmd_file_name)
+{
+ CMD *cmd;
+ int modified = 0;
+ CMD_FILE *cmd_file = NULL;
+ cmd_file = cmd_file_create(cmd_file_name);
+
+ while ((cmd = cmd_file->cmd_ops.get_cmd(cmd_file->fd)) != NULL) {
+
+ /*
+ * Now, apply the requests to the tree ...
+ */
+ switch (cmd->cmd) {
+ case CMD_ADD_KEY: {
+ REG_KEY *tmp = NULL;
+ tmp = reg_open_key(reg_get_root(regf), cmd->key);
+ /* If we found it, apply the other bits, else create such a key */
+ if (!tmp) {
+ if(reg_key_add_name(reg_get_root(regf), cmd->key)) {
+ tmp = reg_open_key(reg_get_root(regf), cmd->key);
+ }
+ modified = 1;
+ }
+
+ while (cmd->val_count) {
+ VAL_SPEC_LIST *val = cmd->val_spec_list;
+ REG_VAL *reg_val = NULL;
+
+ if (val->type == REG_DELETE) {
+ reg_val = reg_key_get_value_by_name( tmp, val->name);
+ reg_val_del(reg_val);
+ modified = 1;
+ }
+ else {
+ /* FIXME
+ reg_val = nt_add_reg_value(tmp, val->name, val->type,
+ val->val); */
+ modified = 1;
+ }
+
+ cmd->val_spec_list = val->next;
+ free_val_spec_list(val);
+ cmd->val_count--;
+ }
+
+ break;
+ }
+
+ case CMD_DEL_KEY:
+ /*
+ * Any value does not matter ...
+ * Find the key if it exists, and delete it ...
+ */
+
+ reg_key_del_recursive(reg_open_key(reg_get_root(regf), cmd->key));
+ modified = 1;
+ break;
+ }
+ }
+ free_cmd(cmd);
+
+ return modified;
+}
+
+int main (int argc, char **argv)
+{
+ uint32 setparms, checkparms;
+ int opt;
+ poptContext pc;
+ REG_KEY *root;
+ const char *location;
+ const char *patch;
+ char *backend = "dir";
+ REG_HANDLE *h;
+ int fullpath = 0, no_values = 0;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"backend", 'b', POPT_ARG_STRING, &backend, 'b', "backend to use", NULL},
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ }
+
+ setup_logging(argv[0], True);
+
+ location = poptGetArg(pc);
+ if(!location) {
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+
+ h = reg_open(backend, location, True);
+ if(!h) {
+ fprintf(stderr, "Unable to open '%s' with backend '%s'\n", location, backend);
+ return 1;
+ }
+
+ poptFreeContext(pc);
+
+ patch = poptGetArg(pc);
+ if(!patch) patch = "/dev/stdin";
+
+ nt_apply_reg_command_file(h, patch);
+
+ return 0;
+}
diff --git a/source4/lib/registry/tools/regshell.c b/source4/lib/registry/tools/regshell.c
new file mode 100644
index 0000000000..9074d1c716
--- /dev/null
+++ b/source4/lib/registry/tools/regshell.c
@@ -0,0 +1,243 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ 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 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"
+
+/*
+ * ck/cd - change key
+ * ls - list values/keys
+ * rmval/rm - remove value
+ * rmkey/rmdir - remove key
+ * mkkey/mkdir - make key
+ * help
+ * exit
+ */
+
+static REG_KEY *cmd_set(REG_KEY *cur, int argc, char **argv)
+{
+ /* FIXME */
+ return NULL;
+}
+
+static REG_KEY *cmd_ck(REG_KEY *cur, int argc, char **argv)
+{
+ REG_KEY *new;
+ if(argc < 2) {
+ new = cur;
+ } else {
+ new = reg_open_key(cur, argv[1]);
+ }
+
+ if(!new) new = cur;
+
+ printf("Current path is: %s\n", reg_key_get_path(new));
+
+ return new;
+}
+
+static REG_KEY *cmd_ls(REG_KEY *cur, int argc, char **argv)
+{
+ int i, num;
+ num = reg_key_num_subkeys(cur);
+ for(i = 0; i < num; i++) {
+ REG_KEY *sub = reg_key_get_subkey_by_index(cur, i);
+ printf("K %s\n", reg_key_name(sub));
+ }
+
+ num = reg_key_num_values(cur);
+ for(i = 0; i < num; i++) {
+ REG_VAL *sub = reg_key_get_value_by_index(cur, i);
+ printf("V %s %s %s\n", reg_val_name(sub), str_regtype(reg_val_type(sub)), reg_val_data_string(sub));
+ }
+
+ return NULL;
+}
+static REG_KEY *cmd_mkkey(REG_KEY *cur, int argc, char **argv)
+{
+ if(argc < 2) {
+ fprintf(stderr, "Usage: mkkey <keyname>\n");
+ return NULL;
+ }
+
+ if(!reg_key_add_name(cur, argv[1])) {
+ fprintf(stderr, "Error adding new subkey '%s'\n", argv[1]);
+ return NULL;
+ }
+
+ fprintf(stderr, "Successfully added new subkey '%s' to '%s'\n", argv[1], reg_key_get_path(cur));
+
+ return NULL;
+}
+
+static REG_KEY *cmd_rmkey(REG_KEY *cur, int argc, char **argv)
+{
+ REG_KEY *key;
+ if(argc < 2) {
+ fprintf(stderr, "Usage: rmkey <name>\n");
+ return NULL;
+ }
+
+ key = reg_open_key(cur, argv[1]);
+ if(!key) {
+ fprintf(stderr, "No such subkey '%s'\n", argv[1]);
+ return NULL;
+ }
+
+ if(!reg_key_del(key)) {
+ fprintf(stderr, "Error deleting '%s'\n", argv[1]);
+ } else {
+ fprintf(stderr, "Successfully deleted '%s'\n", argv[1]);
+ }
+
+ return NULL;
+}
+
+static REG_KEY *cmd_rmval(REG_KEY *cur, int argc, char **argv)
+{
+ REG_VAL *val;
+ if(argc < 2) {
+ fprintf(stderr, "Usage: rmval <valuename>\n");
+ return NULL;
+ }
+
+ val = reg_key_get_value_by_name(cur, argv[1]);
+ if(!val) {
+ fprintf(stderr, "No such value '%s'\n", argv[1]);
+ return NULL;
+ }
+
+ if(!reg_val_del(val)) {
+ fprintf(stderr, "Error deleting value '%s'\n", argv[1]);
+ } else {
+ fprintf(stderr, "Successfully deleted value '%s'\n", argv[1]);
+ }
+
+ return NULL;
+}
+
+static REG_KEY *cmd_exit(REG_KEY *cur, int argc, char **argv)
+{
+ exit(0);
+ return NULL;
+}
+
+static REG_KEY *cmd_help(REG_KEY *, int, char **);
+
+struct {
+ const char *name;
+ const char *alias;
+ const char *help;
+ REG_KEY *(*handle)(REG_KEY *, int argc, char **argv);
+} regshell_cmds[] = {
+ {"ck", "cd", "Change current key", cmd_ck },
+ {"list", "ls", "List values/keys in current key", cmd_ls },
+ {"mkkey", "mkdir", "Make new key", cmd_mkkey },
+ {"rmval", "rm", "Remove value", cmd_rmval },
+ {"rmkey", "rmdir", "Remove key", cmd_rmkey },
+ {"set", "update", "Update value", cmd_set },
+ {"help", "?", "Help", cmd_help },
+ {"exit", "quit", "Exit", cmd_exit },
+ {NULL }
+};
+
+static REG_KEY *cmd_help(REG_KEY *cur, 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;
+}
+
+REG_KEY *process_cmd(REG_KEY *k, char *line)
+{
+ int argc;
+ char **argv = NULL;
+ int ret, i;
+
+ if ((ret = poptParseArgvString(line, &argc, (const char ***) &argv)) != 0) {
+ fprintf(stderr, "regshell: %s\n", poptStrerror(ret));
+ return k;
+ }
+
+ 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(k, argc, argv);
+ }
+ }
+
+ fprintf(stderr, "No such command '%s'\n", argv[0]);
+
+ return k;
+}
+
+int main (int argc, char **argv)
+{
+ uint32 setparms, checkparms;
+ int opt;
+ char *backend = "dir";
+ REG_KEY *curkey = NULL;;
+ poptContext pc;
+ REG_HANDLE *h;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"backend", 'b', POPT_ARG_STRING, &backend, 0, "backend to use", NULL},
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ }
+
+ h = reg_open(backend, poptPeekArg(pc), True);
+ if(!h) {
+ fprintf(stderr, "Unable to open '%s' with backend '%s'\n", poptGetArg(pc), backend);
+ return 1;
+ }
+ poptFreeContext(pc);
+
+ setup_logging("regtree", True);
+
+ curkey = reg_get_root(h);
+
+ if(!curkey) return 1;
+
+ while(True) {
+ char *line, *prompt;
+
+ asprintf(&prompt, "%s> ", reg_key_get_path(curkey));
+
+ line = smb_readline(prompt, NULL, NULL);
+
+ if(!line)
+ break;
+
+ if(line[0] != '\n') {
+ REG_KEY *new = process_cmd(curkey, line);
+ if(new)curkey = new;
+ }
+ }
+
+ return 0;
+}
diff --git a/source4/lib/registry/tools/regtree.c b/source4/lib/registry/tools/regtree.c
new file mode 100644
index 0000000000..80cbccf48f
--- /dev/null
+++ b/source4/lib/registry/tools/regtree.c
@@ -0,0 +1,91 @@
+/*
+ Unix SMB/CIFS implementation.
+ simple registry frontend
+
+ 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 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"
+
+void print_tree(int l, REG_KEY *p, int fullpath, int novals)
+{
+ int num_subkeys, i, num_values;
+
+ for(i = 0; i < l; i++) putchar(' ');
+ if(fullpath) printf("%s\n", reg_key_get_path(p));
+ else printf("%s\n", reg_key_name(p));
+
+ num_subkeys = reg_key_num_subkeys(p);
+ for(i = 0; i < num_subkeys; i++) {
+ REG_KEY *subkey = reg_key_get_subkey_by_index(p, i);
+ print_tree(l+1, subkey, fullpath, novals);
+ reg_key_free(subkey);
+ }
+
+ if(!novals) {
+ num_values = reg_key_num_values(p);
+ for(i = 0; i < num_values; i++) {
+ int j;
+ char *desc;
+ REG_VAL *value = reg_key_get_value_by_index(p, i);
+ for(j = 0; j < l+1; j++) putchar(' ');
+ desc = reg_val_description(value);
+ printf("%s\n", desc);
+ free(desc);
+ reg_val_free(value);
+ }
+ }
+}
+
+int main (int argc, char **argv)
+{
+ uint32 setparms, checkparms;
+ int opt;
+ char *backend = "dir";
+ poptContext pc;
+ REG_KEY *root;
+ REG_HANDLE *h;
+ int fullpath = 0, no_values = 0;
+ 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},
+ {"no-values", 'V', POPT_ARG_NONE, &no_values, 0, "don't show values", NULL},
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ }
+
+ setup_logging("regtree", True);
+
+ h = reg_open(backend, poptPeekArg(pc), True);
+ if(!h) {
+ fprintf(stderr, "Unable to open '%s' with backend '%s'\n", poptGetArg(pc), backend);
+ return 1;
+ }
+ poptFreeContext(pc);
+
+ root = reg_get_root(h);
+ if(!root) return 1;
+
+ print_tree(0, root, fullpath, no_values);
+
+ return 0;
+}
diff --git a/source4/lib/registry/winregistry.pc.in b/source4/lib/registry/winregistry.pc.in
new file mode 100644
index 0000000000..ad134da236
--- /dev/null
+++ b/source4/lib/registry/winregistry.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: winregistry
+Description: Windows registry library
+Version: @PACKAGE_VERSION@
+Libs: @LIBS@ @REG_LIBS@ -L${prefix}/lib -lwinregistry
+Cflags: -I${includedir} @CFLAGS@ @CFLAGS@