diff options
author | Jelmer Vernooij <jelmer@samba.org> | 2004-04-04 16:24:08 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 12:50:33 -0500 |
commit | c424c2b857fe08587eb81a5c5e3625545119d1c2 (patch) | |
tree | 03505d09ccd72cdfd1218066d355e2d01d403bd0 /source4/lib/registry | |
parent | 3855ee0164d1c8ff3c3c4ba8a5556d8cfb6546b3 (diff) | |
download | samba-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')
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", ®_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", ®_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", ®_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, ®f->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 *)®_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 *)®_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(®f_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(®f_hdr->tim1,0), IVAL(®f_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(®f_hdr->first_key, 0))); + + regf->first_key = (NK_HDR *)LOCN(regf->base, IVAL(®f_hdr->first_key,0)); + DEBUGADD(2, ("First Key Offset: %0X\n", + IVAL(®f_hdr->first_key, 0))); + + DEBUGADD(2, ("Data Block Size: %d\n", + IVAL(®f_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", ®_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", ®_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", ®_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@ |