diff options
Diffstat (limited to 'source3/registry')
22 files changed, 9554 insertions, 0 deletions
diff --git a/source3/registry/reg_api.c b/source3/registry/reg_api.c new file mode 100644 index 0000000000..e9a7145255 --- /dev/null +++ b/source3/registry/reg_api.c @@ -0,0 +1,1247 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Volker Lendecke 2006 + * Copyright (C) Michael Adam 2007-2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Attempt to wrap the existing API in a more winreg.idl-like way */ + +/* + * Here is a list of winreg.idl functions and corresponding implementations + * provided here: + * + * 0x00 winreg_OpenHKCR + * 0x01 winreg_OpenHKCU + * 0x02 winreg_OpenHKLM + * 0x03 winreg_OpenHKPD + * 0x04 winreg_OpenHKU + * 0x05 winreg_CloseKey + * 0x06 winreg_CreateKey reg_createkey + * 0x07 winreg_DeleteKey reg_deletekey + * 0x08 winreg_DeleteValue reg_deletevalue + * 0x09 winreg_EnumKey reg_enumkey + * 0x0a winreg_EnumValue reg_enumvalue + * 0x0b winreg_FlushKey + * 0x0c winreg_GetKeySecurity reg_getkeysecurity + * 0x0d winreg_LoadKey + * 0x0e winreg_NotifyChangeKeyValue + * 0x0f winreg_OpenKey reg_openkey + * 0x10 winreg_QueryInfoKey reg_queryinfokey + * 0x11 winreg_QueryValue reg_queryvalue + * 0x12 winreg_ReplaceKey + * 0x13 winreg_RestoreKey reg_restorekey + * 0x14 winreg_SaveKey reg_savekey + * 0x15 winreg_SetKeySecurity reg_setkeysecurity + * 0x16 winreg_SetValue reg_setvalue + * 0x17 winreg_UnLoadKey + * 0x18 winreg_InitiateSystemShutdown + * 0x19 winreg_AbortSystemShutdown + * 0x1a winreg_GetVersion reg_getversion + * 0x1b winreg_OpenHKCC + * 0x1c winreg_OpenHKDD + * 0x1d winreg_QueryMultipleValues + * 0x1e winreg_InitiateSystemShutdownEx + * 0x1f winreg_SaveKeyEx + * 0x20 winreg_OpenHKPT + * 0x21 winreg_OpenHKPN + * 0x22 winreg_QueryMultipleValues2 + * + */ + +#include "includes.h" +#include "regfio.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + + +/********************************************************************** + * Helper functions + **********************************************************************/ + +static WERROR fill_value_cache(struct registry_key *key) +{ + if (key->values != NULL) { + if (!reg_values_need_update(key->key, key->values)) { + return WERR_OK; + } + } + + if (!(key->values = TALLOC_ZERO_P(key, REGVAL_CTR))) { + return WERR_NOMEM; + } + if (fetch_reg_values(key->key, key->values) == -1) { + TALLOC_FREE(key->values); + return WERR_BADFILE; + } + + return WERR_OK; +} + +static WERROR fill_subkey_cache(struct registry_key *key) +{ + if (key->subkeys != NULL) { + if (!reg_subkeys_need_update(key->key, key->subkeys)) { + return WERR_OK; + } + } + + if (!(key->subkeys = TALLOC_ZERO_P(key, REGSUBKEY_CTR))) { + return WERR_NOMEM; + } + + if (fetch_reg_keys(key->key, key->subkeys) == -1) { + TALLOC_FREE(key->subkeys); + return WERR_NO_MORE_ITEMS; + } + + return WERR_OK; +} + +static int regkey_destructor(REGISTRY_KEY *key) +{ + return regdb_close(); +} + +static WERROR regkey_open_onelevel(TALLOC_CTX *mem_ctx, + struct registry_key *parent, + const char *name, + const struct nt_user_token *token, + uint32 access_desired, + struct registry_key **pregkey) +{ + WERROR result = WERR_OK; + struct registry_key *regkey; + REGISTRY_KEY *key; + REGSUBKEY_CTR *subkeys = NULL; + + DEBUG(7,("regkey_open_onelevel: name = [%s]\n", name)); + + SMB_ASSERT(strchr(name, '\\') == NULL); + + if (!(regkey = TALLOC_ZERO_P(mem_ctx, struct registry_key)) || + !(regkey->token = dup_nt_token(regkey, token)) || + !(regkey->key = TALLOC_ZERO_P(regkey, REGISTRY_KEY))) { + result = WERR_NOMEM; + goto done; + } + + if ( !(W_ERROR_IS_OK(result = regdb_open())) ) { + goto done; + } + + key = regkey->key; + talloc_set_destructor(key, regkey_destructor); + + /* initialization */ + + key->type = REG_KEY_GENERIC; + + if (name[0] == '\0') { + /* + * Open a copy of the parent key + */ + if (!parent) { + result = WERR_BADFILE; + goto done; + } + key->name = talloc_strdup(key, parent->key->name); + } + else { + /* + * Normal subkey open + */ + key->name = talloc_asprintf(key, "%s%s%s", + parent ? parent->key->name : "", + parent ? "\\": "", + name); + } + + if (key->name == NULL) { + result = WERR_NOMEM; + goto done; + } + + /* Tag this as a Performance Counter Key */ + + if( StrnCaseCmp(key->name, KEY_HKPD, strlen(KEY_HKPD)) == 0 ) + key->type = REG_KEY_HKPD; + + /* Look up the table of registry I/O operations */ + + if ( !(key->ops = reghook_cache_find( key->name )) ) { + DEBUG(0,("reg_open_onelevel: Failed to assign " + "REGISTRY_OPS to [%s]\n", key->name )); + result = WERR_BADFILE; + goto done; + } + + /* check if the path really exists; failed is indicated by -1 */ + /* if the subkey count failed, bail out */ + + if ( !(subkeys = TALLOC_ZERO_P( key, REGSUBKEY_CTR )) ) { + result = WERR_NOMEM; + goto done; + } + + if ( fetch_reg_keys( key, subkeys ) == -1 ) { + result = WERR_BADFILE; + goto done; + } + + TALLOC_FREE( subkeys ); + + if ( !regkey_access_check( key, access_desired, &key->access_granted, + token ) ) { + result = WERR_ACCESS_DENIED; + goto done; + } + + *pregkey = regkey; + result = WERR_OK; + +done: + if ( !W_ERROR_IS_OK(result) ) { + TALLOC_FREE(regkey); + } + + return result; +} + +WERROR reg_openhive(TALLOC_CTX *mem_ctx, const char *hive, + uint32 desired_access, + const struct nt_user_token *token, + struct registry_key **pkey) +{ + SMB_ASSERT(hive != NULL); + SMB_ASSERT(hive[0] != '\0'); + SMB_ASSERT(strchr(hive, '\\') == NULL); + + return regkey_open_onelevel(mem_ctx, NULL, hive, token, desired_access, + pkey); +} + + +/********************************************************************** + * The API functions + **********************************************************************/ + +WERROR reg_openkey(TALLOC_CTX *mem_ctx, struct registry_key *parent, + const char *name, uint32 desired_access, + struct registry_key **pkey) +{ + struct registry_key *direct_parent = parent; + WERROR err; + char *p, *path, *to_free; + size_t len; + + if (!(path = SMB_STRDUP(name))) { + return WERR_NOMEM; + } + to_free = path; + + len = strlen(path); + + if ((len > 0) && (path[len-1] == '\\')) { + path[len-1] = '\0'; + } + + while ((p = strchr(path, '\\')) != NULL) { + char *name_component; + struct registry_key *tmp; + + if (!(name_component = SMB_STRNDUP(path, (p - path)))) { + err = WERR_NOMEM; + goto error; + } + + err = regkey_open_onelevel(mem_ctx, direct_parent, + name_component, parent->token, + SEC_RIGHTS_ENUM_SUBKEYS, &tmp); + SAFE_FREE(name_component); + + if (!W_ERROR_IS_OK(err)) { + goto error; + } + if (direct_parent != parent) { + TALLOC_FREE(direct_parent); + } + + direct_parent = tmp; + path = p+1; + } + + err = regkey_open_onelevel(mem_ctx, direct_parent, path, parent->token, + desired_access, pkey); + error: + if (direct_parent != parent) { + TALLOC_FREE(direct_parent); + } + SAFE_FREE(to_free); + return err; +} + +WERROR reg_enumkey(TALLOC_CTX *mem_ctx, struct registry_key *key, + uint32 idx, char **name, NTTIME *last_write_time) +{ + WERROR err; + + if (!(key->key->access_granted & SEC_RIGHTS_ENUM_SUBKEYS)) { + return WERR_ACCESS_DENIED; + } + + if (!W_ERROR_IS_OK(err = fill_subkey_cache(key))) { + return err; + } + + if (idx >= key->subkeys->num_subkeys) { + return WERR_NO_MORE_ITEMS; + } + + if (!(*name = talloc_strdup(mem_ctx, key->subkeys->subkeys[idx]))) { + return WERR_NOMEM; + } + + if (last_write_time) { + *last_write_time = 0; + } + + return WERR_OK; +} + +WERROR reg_enumvalue(TALLOC_CTX *mem_ctx, struct registry_key *key, + uint32 idx, char **pname, struct registry_value **pval) +{ + struct registry_value *val; + WERROR err; + + if (!(key->key->access_granted & SEC_RIGHTS_QUERY_VALUE)) { + return WERR_ACCESS_DENIED; + } + + if (!(W_ERROR_IS_OK(err = fill_value_cache(key)))) { + return err; + } + + if (idx >= key->values->num_values) { + return WERR_NO_MORE_ITEMS; + } + + err = registry_pull_value(mem_ctx, &val, + key->values->values[idx]->type, + key->values->values[idx]->data_p, + key->values->values[idx]->size, + key->values->values[idx]->size); + if (!W_ERROR_IS_OK(err)) { + return err; + } + + if (pname + && !(*pname = talloc_strdup( + mem_ctx, key->values->values[idx]->valuename))) { + SAFE_FREE(val); + return WERR_NOMEM; + } + + *pval = val; + return WERR_OK; +} + +WERROR reg_queryvalue(TALLOC_CTX *mem_ctx, struct registry_key *key, + const char *name, struct registry_value **pval) +{ + WERROR err; + uint32 i; + + if (!(key->key->access_granted & SEC_RIGHTS_QUERY_VALUE)) { + return WERR_ACCESS_DENIED; + } + + if (!(W_ERROR_IS_OK(err = fill_value_cache(key)))) { + return err; + } + + for (i=0; i<key->values->num_values; i++) { + if (strequal(key->values->values[i]->valuename, name)) { + return reg_enumvalue(mem_ctx, key, i, NULL, pval); + } + } + + return WERR_BADFILE; +} + +WERROR reg_queryinfokey(struct registry_key *key, uint32_t *num_subkeys, + uint32_t *max_subkeylen, uint32_t *max_subkeysize, + uint32_t *num_values, uint32_t *max_valnamelen, + uint32_t *max_valbufsize, uint32_t *secdescsize, + NTTIME *last_changed_time) +{ + uint32 i, max_size; + size_t max_len; + TALLOC_CTX *mem_ctx; + WERROR err; + struct security_descriptor *secdesc; + + if (!(key->key->access_granted & SEC_RIGHTS_QUERY_VALUE)) { + return WERR_ACCESS_DENIED; + } + + if (!W_ERROR_IS_OK(fill_subkey_cache(key)) || + !W_ERROR_IS_OK(fill_value_cache(key))) { + return WERR_BADFILE; + } + + max_len = 0; + for (i=0; i<key->subkeys->num_subkeys; i++) { + max_len = MAX(max_len, strlen(key->subkeys->subkeys[i])); + } + + *num_subkeys = key->subkeys->num_subkeys; + *max_subkeylen = max_len; + *max_subkeysize = 0; /* Class length? */ + + max_len = 0; + max_size = 0; + for (i=0; i<key->values->num_values; i++) { + max_len = MAX(max_len, + strlen(key->values->values[i]->valuename)); + max_size = MAX(max_size, key->values->values[i]->size); + } + + *num_values = key->values->num_values; + *max_valnamelen = max_len; + *max_valbufsize = max_size; + + if (!(mem_ctx = talloc_new(key))) { + return WERR_NOMEM; + } + + err = regkey_get_secdesc(mem_ctx, key->key, &secdesc); + if (!W_ERROR_IS_OK(err)) { + TALLOC_FREE(mem_ctx); + return err; + } + + *secdescsize = ndr_size_security_descriptor(secdesc, 0); + TALLOC_FREE(mem_ctx); + + *last_changed_time = 0; + + return WERR_OK; +} + +WERROR reg_createkey(TALLOC_CTX *ctx, struct registry_key *parent, + const char *subkeypath, uint32 desired_access, + struct registry_key **pkey, + enum winreg_CreateAction *paction) +{ + struct registry_key *key = parent; + struct registry_key *create_parent; + TALLOC_CTX *mem_ctx; + char *path, *end; + WERROR err; + + if (!(mem_ctx = talloc_new(ctx))) return WERR_NOMEM; + + if (!(path = talloc_strdup(mem_ctx, subkeypath))) { + err = WERR_NOMEM; + goto done; + } + + while ((end = strchr(path, '\\')) != NULL) { + struct registry_key *tmp; + enum winreg_CreateAction action; + + *end = '\0'; + + err = reg_createkey(mem_ctx, key, path, + SEC_RIGHTS_ENUM_SUBKEYS, &tmp, &action); + if (!W_ERROR_IS_OK(err)) { + goto done; + } + + if (key != parent) { + TALLOC_FREE(key); + } + + key = tmp; + path = end+1; + } + + /* + * At this point, "path" contains the one-element subkey of "key". We + * can try to open it. + */ + + err = reg_openkey(ctx, key, path, desired_access, pkey); + if (W_ERROR_IS_OK(err)) { + if (paction != NULL) { + *paction = REG_OPENED_EXISTING_KEY; + } + goto done; + } + + if (!W_ERROR_EQUAL(err, WERR_BADFILE)) { + /* + * Something but "notfound" has happened, so bail out + */ + goto done; + } + + /* + * We have to make a copy of the current key, as we opened it only + * with ENUM_SUBKEY access. + */ + + err = reg_openkey(mem_ctx, key, "", SEC_RIGHTS_CREATE_SUBKEY, + &create_parent); + if (!W_ERROR_IS_OK(err)) { + goto done; + } + + /* + * Actually create the subkey + */ + + err = fill_subkey_cache(create_parent); + if (!W_ERROR_IS_OK(err)) goto done; + + err = regsubkey_ctr_addkey(create_parent->subkeys, path); + if (!W_ERROR_IS_OK(err)) goto done; + + if (!store_reg_keys(create_parent->key, create_parent->subkeys)) { + TALLOC_FREE(create_parent->subkeys); + err = WERR_REG_IO_FAILURE; + goto done; + } + + /* + * Now open the newly created key + */ + + err = reg_openkey(ctx, create_parent, path, desired_access, pkey); + if (W_ERROR_IS_OK(err) && (paction != NULL)) { + *paction = REG_CREATED_NEW_KEY; + } + + done: + TALLOC_FREE(mem_ctx); + return err; +} + +WERROR reg_deletekey(struct registry_key *parent, const char *path) +{ + WERROR err; + TALLOC_CTX *mem_ctx; + char *name, *end; + int num_subkeys; + struct registry_key *tmp_key, *key; + + if (!(mem_ctx = talloc_init("reg_createkey"))) return WERR_NOMEM; + + if (!(name = talloc_strdup(mem_ctx, path))) { + err = WERR_NOMEM; + goto error; + } + + /* check if the key has subkeys */ + err = reg_openkey(mem_ctx, parent, name, REG_KEY_READ, &key); + if (!W_ERROR_IS_OK(err)) { + goto error; + } + if (!W_ERROR_IS_OK(err = fill_subkey_cache(key))) { + goto error; + } + if (key->subkeys->num_subkeys > 0) { + err = WERR_ACCESS_DENIED; + goto error; + } + + /* no subkeys - proceed with delete */ + if ((end = strrchr(name, '\\')) != NULL) { + *end = '\0'; + + err = reg_openkey(mem_ctx, parent, name, + SEC_RIGHTS_CREATE_SUBKEY, &tmp_key); + if (!W_ERROR_IS_OK(err)) { + goto error; + } + + parent = tmp_key; + name = end+1; + } + + if (name[0] == '\0') { + err = WERR_INVALID_PARAM; + goto error; + } + + if (!W_ERROR_IS_OK(err = fill_subkey_cache(parent))) { + goto error; + } + + num_subkeys = parent->subkeys->num_subkeys; + + if (regsubkey_ctr_delkey(parent->subkeys, name) == num_subkeys) { + err = WERR_BADFILE; + goto error; + } + + if (!store_reg_keys(parent->key, parent->subkeys)) { + TALLOC_FREE(parent->subkeys); + err = WERR_REG_IO_FAILURE; + goto error; + } + + regkey_set_secdesc(key->key, NULL); + + err = WERR_OK; + + error: + TALLOC_FREE(mem_ctx); + return err; +} + +WERROR reg_setvalue(struct registry_key *key, const char *name, + const struct registry_value *val) +{ + WERROR err; + DATA_BLOB value_data; + int res; + + if (!(key->key->access_granted & SEC_RIGHTS_SET_VALUE)) { + return WERR_ACCESS_DENIED; + } + + if (!W_ERROR_IS_OK(err = fill_value_cache(key))) { + return err; + } + + err = registry_push_value(key, val, &value_data); + if (!W_ERROR_IS_OK(err)) { + return err; + } + + res = regval_ctr_addvalue(key->values, name, val->type, + (char *)value_data.data, value_data.length); + TALLOC_FREE(value_data.data); + + if (res == 0) { + TALLOC_FREE(key->values); + return WERR_NOMEM; + } + + if (!store_reg_values(key->key, key->values)) { + TALLOC_FREE(key->values); + return WERR_REG_IO_FAILURE; + } + + return WERR_OK; +} + +static WERROR reg_value_exists(struct registry_key *key, const char *name) +{ + int i; + + for (i=0; i<key->values->num_values; i++) { + if (strequal(key->values->values[i]->valuename, name)) { + return WERR_OK; + } + } + + return WERR_BADFILE; +} + +WERROR reg_deletevalue(struct registry_key *key, const char *name) +{ + WERROR err; + + if (!(key->key->access_granted & SEC_RIGHTS_SET_VALUE)) { + return WERR_ACCESS_DENIED; + } + + if (!W_ERROR_IS_OK(err = fill_value_cache(key))) { + return err; + } + + err = reg_value_exists(key, name); + if (!W_ERROR_IS_OK(err)) { + return err; + } + + regval_ctr_delvalue(key->values, name); + + if (!store_reg_values(key->key, key->values)) { + TALLOC_FREE(key->values); + return WERR_REG_IO_FAILURE; + } + + return WERR_OK; +} + +WERROR reg_getkeysecurity(TALLOC_CTX *mem_ctx, struct registry_key *key, + struct security_descriptor **psecdesc) +{ + return regkey_get_secdesc(mem_ctx, key->key, psecdesc); +} + +WERROR reg_setkeysecurity(struct registry_key *key, + struct security_descriptor *psecdesc) +{ + return regkey_set_secdesc(key->key, psecdesc); +} + +WERROR reg_getversion(uint32_t *version) +{ + if (version == NULL) { + return WERR_INVALID_PARAM; + } + + *version = 0x00000005; /* Windows 2000 registry API version */ + return WERR_OK; +} + +/******************************************************************* + Note: topkeypat is the *full* path that this *key will be + loaded into (including the name of the key) + ********************************************************************/ + +static WERROR reg_load_tree(REGF_FILE *regfile, const char *topkeypath, + REGF_NK_REC *key) +{ + REGF_NK_REC *subkey; + REGISTRY_KEY registry_key; + REGVAL_CTR *values; + REGSUBKEY_CTR *subkeys; + int i; + char *path = NULL; + WERROR result = WERR_OK; + + /* initialize the REGISTRY_KEY structure */ + + registry_key.ops = reghook_cache_find(topkeypath); + if (!registry_key.ops) { + DEBUG(0, ("reg_load_tree: Failed to assign REGISTRY_OPS " + "to [%s]\n", topkeypath)); + return WERR_BADFILE; + } + + registry_key.name = talloc_strdup(regfile->mem_ctx, topkeypath); + if (!registry_key.name) { + DEBUG(0, ("reg_load_tree: Talloc failed for reg_key.name!\n")); + return WERR_NOMEM; + } + + /* now start parsing the values and subkeys */ + + subkeys = TALLOC_ZERO_P(regfile->mem_ctx, REGSUBKEY_CTR); + if (subkeys == NULL) { + return WERR_NOMEM; + } + + values = TALLOC_ZERO_P(subkeys, REGVAL_CTR); + if (values == NULL) { + return WERR_NOMEM; + } + + /* copy values into the REGVAL_CTR */ + + for (i=0; i<key->num_values; i++) { + regval_ctr_addvalue(values, key->values[i].valuename, + key->values[i].type, + (char*)key->values[i].data, + (key->values[i].data_size & ~VK_DATA_IN_OFFSET)); + } + + /* copy subkeys into the REGSUBKEY_CTR */ + + key->subkey_index = 0; + while ((subkey = regfio_fetch_subkey( regfile, key ))) { + result = regsubkey_ctr_addkey(subkeys, subkey->keyname); + if (!W_ERROR_IS_OK(result)) { + TALLOC_FREE(subkeys); + return result; + } + } + + /* write this key and values out */ + + if (!store_reg_values(®istry_key, values) + || !store_reg_keys(®istry_key, subkeys)) + { + DEBUG(0,("reg_load_tree: Failed to load %s!\n", topkeypath)); + result = WERR_REG_IO_FAILURE; + } + + TALLOC_FREE(subkeys); + + if (!W_ERROR_IS_OK(result)) { + return result; + } + + /* now continue to load each subkey registry tree */ + + key->subkey_index = 0; + while ((subkey = regfio_fetch_subkey(regfile, key))) { + path = talloc_asprintf(regfile->mem_ctx, + "%s\\%s", + topkeypath, + subkey->keyname); + if (path == NULL) { + return WERR_NOMEM; + } + result = reg_load_tree(regfile, path, subkey); + if (!W_ERROR_IS_OK(result)) { + break; + } + } + + return result; +} + +/******************************************************************* + ********************************************************************/ + +static WERROR restore_registry_key(REGISTRY_KEY *krecord, const char *fname) +{ + REGF_FILE *regfile; + REGF_NK_REC *rootkey; + WERROR result; + + /* open the registry file....fail if the file already exists */ + + regfile = regfio_open(fname, (O_RDONLY), 0); + if (regfile == NULL) { + DEBUG(0, ("restore_registry_key: failed to open \"%s\" (%s)\n", + fname, strerror(errno))); + return ntstatus_to_werror(map_nt_error_from_unix(errno)); + } + + /* get the rootkey from the regf file and then load the tree + via recursive calls */ + + if (!(rootkey = regfio_rootkey(regfile))) { + regfio_close(regfile); + return WERR_REG_FILE_INVALID; + } + + result = reg_load_tree(regfile, krecord->name, rootkey); + + /* cleanup */ + + regfio_close(regfile); + + return result; +} + +WERROR reg_restorekey(struct registry_key *key, const char *fname) +{ + return restore_registry_key(key->key, fname); +} + +/******************************************************************** +********************************************************************/ + +static WERROR reg_write_tree(REGF_FILE *regfile, const char *keypath, + REGF_NK_REC *parent) +{ + REGF_NK_REC *key; + REGVAL_CTR *values; + REGSUBKEY_CTR *subkeys; + int i, num_subkeys; + char *key_tmp = NULL; + char *keyname, *parentpath; + char *subkeypath = NULL; + char *subkeyname; + REGISTRY_KEY registry_key; + WERROR result = WERR_OK; + SEC_DESC *sec_desc = NULL; + + if (!regfile) { + return WERR_GENERAL_FAILURE; + } + + if (!keypath) { + return WERR_OBJECT_PATH_INVALID; + } + + /* split up the registry key path */ + + key_tmp = talloc_strdup(regfile->mem_ctx, keypath); + if (!key_tmp) { + return WERR_NOMEM; + } + if (!reg_split_key(key_tmp, &parentpath, &keyname)) { + return WERR_OBJECT_PATH_INVALID; + } + + if (!keyname) { + keyname = parentpath; + } + + /* we need a REGISTRY_KEY object here to enumerate subkeys and values */ + + ZERO_STRUCT(registry_key); + + registry_key.name = talloc_strdup(regfile->mem_ctx, keypath); + if (registry_key.name == NULL) { + return WERR_NOMEM; + } + + registry_key.ops = reghook_cache_find(registry_key.name); + if (registry_key.ops == NULL) { + return WERR_BADFILE; + } + + /* lookup the values and subkeys */ + + subkeys = TALLOC_ZERO_P(regfile->mem_ctx, REGSUBKEY_CTR); + if (subkeys == NULL) { + return WERR_NOMEM; + } + + values = TALLOC_ZERO_P(subkeys, REGVAL_CTR); + if (values == NULL) { + return WERR_NOMEM; + } + + fetch_reg_keys(®istry_key, subkeys); + fetch_reg_values(®istry_key, values); + + result = regkey_get_secdesc(regfile->mem_ctx, ®istry_key, &sec_desc); + if (!W_ERROR_IS_OK(result)) { + goto done; + } + + /* write out this key */ + + key = regfio_write_key(regfile, keyname, values, subkeys, sec_desc, + parent); + if (key == NULL) { + result = WERR_CAN_NOT_COMPLETE; + goto done; + } + + /* write each one of the subkeys out */ + + num_subkeys = regsubkey_ctr_numkeys(subkeys); + for (i=0; i<num_subkeys; i++) { + subkeyname = regsubkey_ctr_specific_key(subkeys, i); + subkeypath = talloc_asprintf(regfile->mem_ctx, "%s\\%s", + keypath, subkeyname); + if (subkeypath == NULL) { + result = WERR_NOMEM; + goto done; + } + result = reg_write_tree(regfile, subkeypath, key); + if (!W_ERROR_IS_OK(result)) + goto done; + } + + DEBUG(6, ("reg_write_tree: wrote key [%s]\n", keypath)); + +done: + TALLOC_FREE(subkeys); + TALLOC_FREE(registry_key.name); + + return result; +} + +static WERROR backup_registry_key(REGISTRY_KEY *krecord, const char *fname) +{ + REGF_FILE *regfile; + WERROR result; + + /* open the registry file....fail if the file already exists */ + + regfile = regfio_open(fname, (O_RDWR|O_CREAT|O_EXCL), + (S_IREAD|S_IWRITE)); + if (regfile == NULL) { + DEBUG(0,("backup_registry_key: failed to open \"%s\" (%s)\n", + fname, strerror(errno) )); + return ntstatus_to_werror(map_nt_error_from_unix(errno)); + } + + /* write the registry tree to the file */ + + result = reg_write_tree(regfile, krecord->name, NULL); + + /* cleanup */ + + regfio_close(regfile); + + return result; +} + +WERROR reg_savekey(struct registry_key *key, const char *fname) +{ + return backup_registry_key(key->key, fname); +} + +/********************************************************************** + * Higher level utility functions + **********************************************************************/ + +WERROR reg_deleteallvalues(struct registry_key *key) +{ + WERROR err; + int i; + + if (!(key->key->access_granted & SEC_RIGHTS_SET_VALUE)) { + return WERR_ACCESS_DENIED; + } + + if (!W_ERROR_IS_OK(err = fill_value_cache(key))) { + return err; + } + + for (i=0; i<key->values->num_values; i++) { + regval_ctr_delvalue(key->values, key->values->values[i]->valuename); + } + + if (!store_reg_values(key->key, key->values)) { + TALLOC_FREE(key->values); + return WERR_REG_IO_FAILURE; + } + + return WERR_OK; +} + +/* + * Utility function to open a complete registry path including the hive prefix. + */ + +WERROR reg_open_path(TALLOC_CTX *mem_ctx, const char *orig_path, + uint32 desired_access, const struct nt_user_token *token, + struct registry_key **pkey) +{ + struct registry_key *hive, *key; + char *path, *p; + WERROR err; + + if (!(path = SMB_STRDUP(orig_path))) { + return WERR_NOMEM; + } + + p = strchr(path, '\\'); + + if ((p == NULL) || (p[1] == '\0')) { + /* + * No key behind the hive, just return the hive + */ + + err = reg_openhive(mem_ctx, path, desired_access, token, + &hive); + if (!W_ERROR_IS_OK(err)) { + SAFE_FREE(path); + return err; + } + SAFE_FREE(path); + *pkey = hive; + return WERR_OK; + } + + *p = '\0'; + + err = reg_openhive(mem_ctx, path, SEC_RIGHTS_ENUM_SUBKEYS, token, + &hive); + if (!W_ERROR_IS_OK(err)) { + SAFE_FREE(path); + return err; + } + + err = reg_openkey(mem_ctx, hive, p+1, desired_access, &key); + + TALLOC_FREE(hive); + SAFE_FREE(path); + + if (!W_ERROR_IS_OK(err)) { + return err; + } + + *pkey = key; + return WERR_OK; +} + +/* + * Utility function to delete a registry key with all its subkeys. + * Note that reg_deletekey returns ACCESS_DENIED when called on a + * key that has subkeys. + */ +static WERROR reg_deletekey_recursive_internal(TALLOC_CTX *ctx, + struct registry_key *parent, + const char *path, + bool del_key) +{ + TALLOC_CTX *mem_ctx = NULL; + WERROR werr = WERR_OK; + struct registry_key *key; + char *subkey_name = NULL; + + mem_ctx = talloc_new(ctx); + if (mem_ctx == NULL) { + werr = WERR_NOMEM; + goto done; + } + + /* recurse through subkeys first */ + werr = reg_openkey(mem_ctx, parent, path, REG_KEY_ALL, &key); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + while (W_ERROR_IS_OK(werr = reg_enumkey(mem_ctx, key, 0, + &subkey_name, NULL))) + { + werr = reg_deletekey_recursive_internal(mem_ctx, key, + subkey_name, + true); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + } + if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { + DEBUG(1, ("reg_deletekey_recursive_internal: " + "Error enumerating subkeys: %s\n", + dos_errstr(werr))); + goto done; + } + + werr = WERR_OK; + + if (del_key) { + /* now delete the actual key */ + werr = reg_deletekey(parent, path); + } + +done: + TALLOC_FREE(mem_ctx); + return werr; +} + +WERROR reg_deletekey_recursive(TALLOC_CTX *ctx, + struct registry_key *parent, + const char *path) +{ + return reg_deletekey_recursive_internal(ctx, parent, path, true); +} + +WERROR reg_deletesubkeys_recursive(TALLOC_CTX *ctx, + struct registry_key *parent, + const char *path) +{ + return reg_deletekey_recursive_internal(ctx, parent, path, false); +} + +#if 0 +/* these two functions are unused. */ + +/** + * Utility function to create a registry key without opening the hive + * before. Assumes the hive already exists. + */ + +WERROR reg_create_path(TALLOC_CTX *mem_ctx, const char *orig_path, + uint32 desired_access, + const struct nt_user_token *token, + enum winreg_CreateAction *paction, + struct registry_key **pkey) +{ + struct registry_key *hive; + char *path, *p; + WERROR err; + + if (!(path = SMB_STRDUP(orig_path))) { + return WERR_NOMEM; + } + + p = strchr(path, '\\'); + + if ((p == NULL) || (p[1] == '\0')) { + /* + * No key behind the hive, just return the hive + */ + + err = reg_openhive(mem_ctx, path, desired_access, token, + &hive); + if (!W_ERROR_IS_OK(err)) { + SAFE_FREE(path); + return err; + } + SAFE_FREE(path); + *pkey = hive; + *paction = REG_OPENED_EXISTING_KEY; + return WERR_OK; + } + + *p = '\0'; + + err = reg_openhive(mem_ctx, path, + (strchr(p+1, '\\') != NULL) ? + SEC_RIGHTS_ENUM_SUBKEYS : SEC_RIGHTS_CREATE_SUBKEY, + token, &hive); + if (!W_ERROR_IS_OK(err)) { + SAFE_FREE(path); + return err; + } + + err = reg_createkey(mem_ctx, hive, p+1, desired_access, pkey, paction); + SAFE_FREE(path); + TALLOC_FREE(hive); + return err; +} + +/* + * Utility function to create a registry key without opening the hive + * before. Will not delete a hive. + */ + +WERROR reg_delete_path(const struct nt_user_token *token, + const char *orig_path) +{ + struct registry_key *hive; + char *path, *p; + WERROR err; + + if (!(path = SMB_STRDUP(orig_path))) { + return WERR_NOMEM; + } + + p = strchr(path, '\\'); + + if ((p == NULL) || (p[1] == '\0')) { + SAFE_FREE(path); + return WERR_INVALID_PARAM; + } + + *p = '\0'; + + err = reg_openhive(NULL, path, + (strchr(p+1, '\\') != NULL) ? + SEC_RIGHTS_ENUM_SUBKEYS : SEC_RIGHTS_CREATE_SUBKEY, + token, &hive); + if (!W_ERROR_IS_OK(err)) { + SAFE_FREE(path); + return err; + } + + err = reg_deletekey(hive, p+1); + SAFE_FREE(path); + TALLOC_FREE(hive); + return err; +} +#endif /* #if 0 */ diff --git a/source3/registry/reg_backend_current_version.c b/source3/registry/reg_backend_current_version.c new file mode 100644 index 0000000000..04cc0ebfa7 --- /dev/null +++ b/source3/registry/reg_backend_current_version.c @@ -0,0 +1,81 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * CurrentVersion registry backend. + * + * This is a virtual overlay, dynamically presenting version information. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +extern REGISTRY_OPS regdb_ops; + +#define KEY_CURRENT_VERSION_NORM "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION" + +static int current_version_fetch_values(const char *key, REGVAL_CTR *values) +{ + const char *sysroot_string = "c:\\Windows"; + fstring sysversion; + fstring value; + uint32 value_length; + char *path = NULL; + TALLOC_CTX *ctx = talloc_tos(); + + path = talloc_strdup(ctx, key); + if (path == NULL) { + return -1; + } + path = normalize_reg_path(ctx, path); + if (path == NULL) { + return -1; + } + + if (strncmp(path, KEY_CURRENT_VERSION_NORM, strlen(path)) != 0) { + return regdb_ops.fetch_values(key, values); + } + + value_length = push_ucs2(value, value, sysroot_string, sizeof(value), + STR_TERMINATE|STR_NOALIGN ); + regval_ctr_addvalue(values, "SystemRoot", REG_SZ, value, value_length); + + fstr_sprintf(sysversion, "%d.%d", lp_major_announce_version(), + lp_minor_announce_version()); + value_length = push_ucs2(value, value, sysversion, sizeof(value), + STR_TERMINATE|STR_NOALIGN); + regval_ctr_addvalue(values, "CurrentVersion", REG_SZ, value, + value_length); + + return regval_ctr_numvals(values); +} + +static int current_version_fetch_subkeys(const char *key, + REGSUBKEY_CTR *subkey_ctr) +{ + return regdb_ops.fetch_subkeys(key, subkey_ctr); +} + +REGISTRY_OPS current_version_reg_ops = { + .fetch_values = current_version_fetch_values, + .fetch_subkeys = current_version_fetch_subkeys, +}; diff --git a/source3/registry/reg_backend_db.c b/source3/registry/reg_backend_db.c new file mode 100644 index 0000000000..6f4c614b9a --- /dev/null +++ b/source3/registry/reg_backend_db.c @@ -0,0 +1,1271 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Implementation of internal registry database functions. */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +static struct db_context *regdb = NULL; +static int regdb_refcount; + +static bool regdb_key_exists(const char *key); +static bool regdb_key_is_base_key(const char *key); + +/* List the deepest path into the registry. All part components will be created.*/ + +/* If you want to have a part of the path controlled by the tdb and part by + a virtual registry db (e.g. printing), then you have to list the deepest path. + For example,"HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion/Print" + allows the reg_db backend to handle everything up to + "HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion" and then we'll hook + the reg_printing backend onto the last component of the path (see + KEY_PRINTING_2K in include/rpc_reg.h) --jerry */ + +static const char *builtin_registry_paths[] = { + KEY_PRINTING_2K, + KEY_PRINTING_PORTS, + KEY_PRINTING, + KEY_SHARES, + KEY_EVENTLOG, + KEY_SMBCONF, + KEY_PERFLIB, + KEY_PERFLIB_009, + KEY_GROUP_POLICY, + KEY_SAMBA_GROUP_POLICY, + KEY_GP_MACHINE_POLICY, + KEY_GP_MACHINE_WIN_POLICY, + KEY_HKCU, + KEY_GP_USER_POLICY, + KEY_GP_USER_WIN_POLICY, + KEY_WINLOGON_GPEXT_PATH, + "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors", + KEY_PROD_OPTIONS, + "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\DefaultUserConfiguration", + KEY_TCPIP_PARAMS, + KEY_NETLOGON_PARAMS, + KEY_HKU, + KEY_HKCR, + KEY_HKPD, + KEY_HKPT, + NULL }; + +struct builtin_regkey_value { + const char *path; + const char *valuename; + uint32 type; + union { + const char *string; + uint32 dw_value; + } data; +}; + +static struct builtin_regkey_value builtin_registry_values[] = { + { KEY_PRINTING_PORTS, + SAMBA_PRINTER_PORT_NAME, REG_SZ, { "" } }, + { KEY_PRINTING_2K, + "DefaultSpoolDirectory", REG_SZ, { "C:\\Windows\\System32\\Spool\\Printers" } }, + { KEY_EVENTLOG, + "DisplayName", REG_SZ, { "Event Log" } }, + { KEY_EVENTLOG, + "ErrorControl", REG_DWORD, { (char*)0x00000001 } }, + { NULL, NULL, 0, { NULL } } +}; + +/** + * Initialize a key in the registry: + * create each component key of the specified path. + */ +static WERROR init_registry_key_internal(const char *add_path) +{ + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + char *path = NULL; + char *base = NULL; + char *remaining = NULL; + char *keyname; + char *subkeyname; + REGSUBKEY_CTR *subkeys; + const char *p, *p2; + + DEBUG(6, ("init_registry_key: Adding [%s]\n", add_path)); + + path = talloc_strdup(frame, add_path); + base = talloc_strdup(frame, ""); + if (!path || !base) { + werr = WERR_NOMEM; + goto fail; + } + p = path; + + while (next_token_talloc(frame, &p, &keyname, "\\")) { + + /* build up the registry path from the components */ + + if (*base) { + base = talloc_asprintf(frame, "%s\\", base); + if (!base) { + werr = WERR_NOMEM; + goto fail; + } + } + base = talloc_asprintf_append(base, "%s", keyname); + if (!base) { + werr = WERR_NOMEM; + goto fail; + } + + /* get the immediate subkeyname (if we have one ) */ + + subkeyname = talloc_strdup(frame, ""); + if (!subkeyname) { + werr = WERR_NOMEM; + goto fail; + } + if (*p) { + remaining = talloc_strdup(frame, p); + if (!remaining) { + werr = WERR_NOMEM; + goto fail; + } + p2 = remaining; + + if (!next_token_talloc(frame, &p2, + &subkeyname, "\\")) + { + subkeyname = talloc_strdup(frame,p2); + if (!subkeyname) { + werr = WERR_NOMEM; + goto fail; + } + } + } + + DEBUG(10,("init_registry_key: Storing key [%s] with " + "subkey [%s]\n", base, + *subkeyname ? subkeyname : "NULL")); + + /* we don't really care if the lookup succeeds or not + * since we are about to update the record. + * We just want any subkeys already present */ + + if (!(subkeys = TALLOC_ZERO_P(frame, REGSUBKEY_CTR))) { + DEBUG(0,("talloc() failure!\n")); + werr = WERR_NOMEM; + goto fail; + } + + regdb_fetch_keys(base, subkeys); + if (*subkeyname) { + werr = regsubkey_ctr_addkey(subkeys, subkeyname); + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + } + if (!regdb_store_keys( base, subkeys)) { + werr = WERR_CAN_NOT_COMPLETE; + goto fail; + } + } + + werr = WERR_OK; + +fail: + TALLOC_FREE(frame); + return werr; +} + +/** + * Initialize a key in the registry: + * create each component key of the specified path, + * wrapped in one db transaction. + */ +WERROR init_registry_key(const char *add_path) +{ + WERROR werr; + + if (regdb_key_exists(add_path)) { + return WERR_OK; + } + + if (regdb->transaction_start(regdb) != 0) { + DEBUG(0, ("init_registry_key: transaction_start failed\n")); + return WERR_REG_IO_FAILURE; + } + + werr = init_registry_key_internal(add_path); + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + + if (regdb->transaction_commit(regdb) != 0) { + DEBUG(0, ("init_registry_key: Could not commit transaction\n")); + return WERR_REG_IO_FAILURE; + } + + return WERR_OK; + +fail: + if (regdb->transaction_cancel(regdb) != 0) { + smb_panic("init_registry_key: transaction_cancel failed\n"); + } + + return werr; +} + +/*********************************************************************** + Open the registry data in the tdb + ***********************************************************************/ + +WERROR init_registry_data(void) +{ + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + REGVAL_CTR *values; + int i; + UNISTR2 data; + + /* + * First, check for the existence of the needed keys and values. + * If all do already exist, we can save the writes. + */ + for (i=0; builtin_registry_paths[i] != NULL; i++) { + if (!regdb_key_exists(builtin_registry_paths[i])) { + goto do_init; + } + } + + for (i=0; builtin_registry_values[i].path != NULL; i++) { + values = TALLOC_ZERO_P(frame, REGVAL_CTR); + if (values == NULL) { + werr = WERR_NOMEM; + goto done; + } + + regdb_fetch_values(builtin_registry_values[i].path, values); + if (!regval_ctr_key_exists(values, + builtin_registry_values[i].valuename)) + { + TALLOC_FREE(values); + goto do_init; + } + + TALLOC_FREE(values); + } + + werr = WERR_OK; + goto done; + +do_init: + + /* + * There are potentially quite a few store operations which are all + * indiviually wrapped in tdb transactions. Wrapping them in a single + * transaction gives just a single transaction_commit() to actually do + * its fsync()s. See tdb/common/transaction.c for info about nested + * transaction behaviour. + */ + + if (regdb->transaction_start(regdb) != 0) { + DEBUG(0, ("init_registry_data: tdb_transaction_start " + "failed\n")); + werr = WERR_REG_IO_FAILURE; + goto done; + } + + /* loop over all of the predefined paths and add each component */ + + for (i=0; builtin_registry_paths[i] != NULL; i++) { + if (regdb_key_exists(builtin_registry_paths[i])) { + continue; + } + werr = init_registry_key_internal(builtin_registry_paths[i]); + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + } + + /* loop over all of the predefined values and add each component */ + + for (i=0; builtin_registry_values[i].path != NULL; i++) { + + values = TALLOC_ZERO_P(frame, REGVAL_CTR); + if (values == NULL) { + werr = WERR_NOMEM; + goto fail; + } + + regdb_fetch_values(builtin_registry_values[i].path, values); + + /* preserve existing values across restarts. Only add new ones */ + + if (!regval_ctr_key_exists(values, + builtin_registry_values[i].valuename)) + { + switch(builtin_registry_values[i].type) { + case REG_DWORD: + regval_ctr_addvalue(values, + builtin_registry_values[i].valuename, + REG_DWORD, + (char*)&builtin_registry_values[i].data.dw_value, + sizeof(uint32)); + break; + + case REG_SZ: + init_unistr2(&data, + builtin_registry_values[i].data.string, + UNI_STR_TERMINATE); + regval_ctr_addvalue(values, + builtin_registry_values[i].valuename, + REG_SZ, + (char*)data.buffer, + data.uni_str_len*sizeof(uint16)); + break; + + default: + DEBUG(0, ("init_registry_data: invalid value " + "type in builtin_registry_values " + "[%d]\n", + builtin_registry_values[i].type)); + } + regdb_store_values(builtin_registry_values[i].path, + values); + } + TALLOC_FREE(values); + } + + if (regdb->transaction_commit(regdb) != 0) { + DEBUG(0, ("init_registry_data: Could not commit " + "transaction\n")); + werr = WERR_REG_IO_FAILURE; + } else { + werr = WERR_OK; + } + + goto done; + +fail: + if (regdb->transaction_cancel(regdb) != 0) { + smb_panic("init_registry_data: tdb_transaction_cancel " + "failed\n"); + } + +done: + TALLOC_FREE(frame); + return werr; +} + +/*********************************************************************** + Open the registry database + ***********************************************************************/ + +WERROR regdb_init(void) +{ + const char *vstring = "INFO/version"; + uint32 vers_id; + WERROR werr; + + if (regdb) { + DEBUG(10, ("regdb_init: incrementing refcount (%d)\n", + regdb_refcount)); + regdb_refcount++; + return WERR_OK; + } + + regdb = db_open(NULL, state_path("registry.tdb"), 0, + REG_TDB_FLAGS, O_RDWR, 0600); + if (!regdb) { + regdb = db_open(NULL, state_path("registry.tdb"), 0, + REG_TDB_FLAGS, O_RDWR|O_CREAT, 0600); + if (!regdb) { + werr = ntstatus_to_werror(map_nt_error_from_unix(errno)); + DEBUG(1,("regdb_init: Failed to open registry %s (%s)\n", + state_path("registry.tdb"), strerror(errno) )); + return werr; + } + + DEBUG(10,("regdb_init: Successfully created registry tdb\n")); + } + + regdb_refcount = 1; + + vers_id = dbwrap_fetch_int32(regdb, vstring); + + if ( vers_id != REGVER_V1 ) { + NTSTATUS status; + /* any upgrade code here if needed */ + DEBUG(10, ("regdb_init: got %s = %d != %d\n", vstring, + vers_id, REGVER_V1)); + status = dbwrap_trans_store_int32(regdb, vstring, REGVER_V1); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("regdb_init: error storing %s = %d: %s\n", + vstring, REGVER_V1, nt_errstr(status))); + return ntstatus_to_werror(status); + } else { + DEBUG(10, ("regdb_init: stored %s = %d\n", + vstring, REGVER_V1)); + } + } + + return WERR_OK; +} + +/*********************************************************************** + Open the registry. Must already have been initialized by regdb_init() + ***********************************************************************/ + +WERROR regdb_open( void ) +{ + WERROR result = WERR_OK; + + if ( regdb ) { + DEBUG(10,("regdb_open: incrementing refcount (%d)\n", regdb_refcount)); + regdb_refcount++; + return WERR_OK; + } + + become_root(); + + regdb = db_open(NULL, state_path("registry.tdb"), 0, + REG_TDB_FLAGS, O_RDWR, 0600); + if ( !regdb ) { + result = ntstatus_to_werror( map_nt_error_from_unix( errno ) ); + DEBUG(0,("regdb_open: Failed to open %s! (%s)\n", + state_path("registry.tdb"), strerror(errno) )); + } + + unbecome_root(); + + regdb_refcount = 1; + DEBUG(10,("regdb_open: refcount reset (%d)\n", regdb_refcount)); + + return result; +} + +/*********************************************************************** + ***********************************************************************/ + +int regdb_close( void ) +{ + if (regdb_refcount == 0) { + return 0; + } + + regdb_refcount--; + + DEBUG(10,("regdb_close: decrementing refcount (%d)\n", regdb_refcount)); + + if ( regdb_refcount > 0 ) + return 0; + + SMB_ASSERT( regdb_refcount >= 0 ); + + TALLOC_FREE(regdb); + return 0; +} + +/*********************************************************************** + return the tdb sequence number of the registry tdb. + this is an indicator for the content of the registry + having changed. it will change upon regdb_init, too, though. + ***********************************************************************/ +int regdb_get_seqnum(void) +{ + return regdb->get_seqnum(regdb); +} + +/*********************************************************************** + Add subkey strings to the registry tdb under a defined key + fmt is the same format as tdb_pack except this function only supports + fstrings + ***********************************************************************/ + +static bool regdb_store_keys_internal(const char *key, REGSUBKEY_CTR *ctr) +{ + TDB_DATA dbuf; + uint8 *buffer = NULL; + int i = 0; + uint32 len, buflen; + bool ret = true; + uint32 num_subkeys = regsubkey_ctr_numkeys(ctr); + char *keyname = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + NTSTATUS status; + + if (!key) { + return false; + } + + keyname = talloc_strdup(ctx, key); + if (!keyname) { + return false; + } + keyname = normalize_reg_path(ctx, keyname); + + /* allocate some initial memory */ + + buffer = (uint8 *)SMB_MALLOC(1024); + if (buffer == NULL) { + return false; + } + buflen = 1024; + len = 0; + + /* store the number of subkeys */ + + len += tdb_pack(buffer+len, buflen-len, "d", num_subkeys); + + /* pack all the strings */ + + for (i=0; i<num_subkeys; i++) { + len += tdb_pack(buffer+len, buflen-len, "f", + regsubkey_ctr_specific_key(ctr, i)); + if (len > buflen) { + /* allocate some extra space */ + buffer = (uint8 *)SMB_REALLOC(buffer, len*2); + if(buffer == NULL) { + DEBUG(0, ("regdb_store_keys: Failed to realloc " + "memory of size [%d]\n", len*2)); + ret = false; + goto done; + } + buflen = len*2; + len = tdb_pack(buffer+len, buflen-len, "f", + regsubkey_ctr_specific_key(ctr, i)); + } + } + + /* finally write out the data */ + + dbuf.dptr = buffer; + dbuf.dsize = len; + status = dbwrap_store_bystring(regdb, keyname, dbuf, TDB_REPLACE); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + +done: + TALLOC_FREE(ctx); + SAFE_FREE(buffer); + return ret; +} + +/*********************************************************************** + Store the new subkey record and create any child key records that + do not currently exist + ***********************************************************************/ + +bool regdb_store_keys(const char *key, REGSUBKEY_CTR *ctr) +{ + int num_subkeys, i; + char *path = NULL; + REGSUBKEY_CTR *subkeys = NULL, *old_subkeys = NULL; + char *oldkeyname = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + NTSTATUS status; + + if (!regdb_key_is_base_key(key) && !regdb_key_exists(key)) { + goto fail; + } + + /* + * fetch a list of the old subkeys so we can determine if anything has + * changed + */ + + if (!(old_subkeys = TALLOC_ZERO_P(ctx, REGSUBKEY_CTR))) { + DEBUG(0,("regdb_store_keys: talloc() failure!\n")); + return false; + } + + regdb_fetch_keys(key, old_subkeys); + + if ((ctr->num_subkeys && old_subkeys->num_subkeys) && + (ctr->num_subkeys == old_subkeys->num_subkeys)) { + + for (i = 0; i<ctr->num_subkeys; i++) { + if (strcmp(ctr->subkeys[i], + old_subkeys->subkeys[i]) != 0) { + break; + } + } + if (i == ctr->num_subkeys) { + /* + * Nothing changed, no point to even start a tdb + * transaction + */ + TALLOC_FREE(old_subkeys); + return true; + } + } + + TALLOC_FREE(old_subkeys); + + if (regdb->transaction_start(regdb) != 0) { + DEBUG(0, ("regdb_store_keys: transaction_start failed\n")); + goto fail; + } + + /* + * Re-fetch the old keys inside the transaction + */ + + if (!(old_subkeys = TALLOC_ZERO_P(ctx, REGSUBKEY_CTR))) { + DEBUG(0,("regdb_store_keys: talloc() failure!\n")); + goto cancel; + } + + regdb_fetch_keys(key, old_subkeys); + + /* + * Make the store operation as safe as possible without transactions: + * + * (1) For each subkey removed from ctr compared with old_subkeys: + * + * (a) First delete the value db entry. + * + * (b) Next delete the secdesc db record. + * + * (c) Then delete the subkey list entry. + * + * (2) Now write the list of subkeys of the parent key, + * deleting removed entries and adding new ones. + * + * (3) Finally create the subkey list entries for the added keys. + * + * This way if we crash half-way in between deleting the subkeys + * and storing the parent's list of subkeys, no old data can pop up + * out of the blue when re-adding keys later on. + */ + + /* (1) delete removed keys' lists (values/secdesc/subkeys) */ + + num_subkeys = regsubkey_ctr_numkeys(old_subkeys); + for (i=0; i<num_subkeys; i++) { + oldkeyname = regsubkey_ctr_specific_key(old_subkeys, i); + + if (regsubkey_ctr_key_exists(ctr, oldkeyname)) { + /* + * It's still around, don't delete + */ + + continue; + } + + /* (a) Delete the value list for this key */ + + path = talloc_asprintf(ctx, "%s/%s/%s", + REG_VALUE_PREFIX, + key, + oldkeyname ); + if (!path) { + goto cancel; + } + path = normalize_reg_path(ctx, path); + if (!path) { + goto cancel; + } + /* Ignore errors here, we might have no values around */ + dbwrap_delete_bystring(regdb, path); + TALLOC_FREE(path); + + /* (b) Delete the secdesc for this key */ + + path = talloc_asprintf(ctx, "%s/%s/%s", + REG_SECDESC_PREFIX, + key, + oldkeyname ); + if (!path) { + goto cancel; + } + path = normalize_reg_path(ctx, path); + if (!path) { + goto cancel; + } + status = dbwrap_delete_bystring(regdb, path); + /* Don't fail if there are no values around. */ + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) + { + DEBUG(1, ("Deleting %s failed: %s\n", path, + nt_errstr(status))); + goto cancel; + } + TALLOC_FREE(path); + + /* (c) Delete the list of subkeys of this key */ + + path = talloc_asprintf(ctx, "%s/%s", key, oldkeyname); + if (!path) { + goto cancel; + } + path = normalize_reg_path(ctx, path); + if (!path) { + goto cancel; + } + status = dbwrap_delete_bystring(regdb, path); + /* Don't fail if the subkey record was not found. */ + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) + { + DEBUG(1, ("Deleting %s failed: %s\n", path, + nt_errstr(status))); + goto cancel; + } + TALLOC_FREE(path); + } + + TALLOC_FREE(old_subkeys); + + /* (2) store the subkey list for the parent */ + + if (!regdb_store_keys_internal(key, ctr) ) { + DEBUG(0,("regdb_store_keys: Failed to store new subkey list " + "for parent [%s]\n", key)); + goto cancel; + } + + /* (3) now create records for any subkeys that don't already exist */ + + num_subkeys = regsubkey_ctr_numkeys(ctr); + + if (num_subkeys == 0) { + if (!(subkeys = TALLOC_ZERO_P(ctx, REGSUBKEY_CTR)) ) { + DEBUG(0,("regdb_store_keys: talloc() failure!\n")); + goto cancel; + } + + if (!regdb_store_keys_internal(key, subkeys)) { + DEBUG(0,("regdb_store_keys: Failed to store " + "new record for key [%s]\n", key)); + goto cancel; + } + TALLOC_FREE(subkeys); + + } + + for (i=0; i<num_subkeys; i++) { + path = talloc_asprintf(ctx, "%s/%s", + key, + regsubkey_ctr_specific_key(ctr, i)); + if (!path) { + goto cancel; + } + if (!(subkeys = TALLOC_ZERO_P(ctx, REGSUBKEY_CTR)) ) { + DEBUG(0,("regdb_store_keys: talloc() failure!\n")); + goto cancel; + } + + if (regdb_fetch_keys( path, subkeys ) == -1) { + /* create a record with 0 subkeys */ + if (!regdb_store_keys_internal(path, subkeys)) { + DEBUG(0,("regdb_store_keys: Failed to store " + "new record for key [%s]\n", path)); + goto cancel; + } + } + + TALLOC_FREE(subkeys); + TALLOC_FREE(path); + } + + if (regdb->transaction_commit(regdb) != 0) { + DEBUG(0, ("regdb_store_keys: Could not commit transaction\n")); + goto fail; + } + + TALLOC_FREE(ctx); + return true; + +cancel: + if (regdb->transaction_cancel(regdb) != 0) { + smb_panic("regdb_store_keys: transaction_cancel failed\n"); + } + +fail: + TALLOC_FREE(ctx); + + return false; +} + + +static TDB_DATA regdb_fetch_key_internal(TALLOC_CTX *mem_ctx, const char *key) +{ + char *path = NULL; + TDB_DATA data; + + path = normalize_reg_path(mem_ctx, key); + if (!path) { + return make_tdb_data(NULL, 0); + } + + data = dbwrap_fetch_bystring(regdb, mem_ctx, path); + + TALLOC_FREE(path); + return data; +} + + +/** + * check whether a given key name represents a base key, + * i.e one without a subkey separator ('/' or '\'). + */ +static bool regdb_key_is_base_key(const char *key) +{ + TALLOC_CTX *mem_ctx = talloc_stackframe(); + bool ret = false; + char *path; + + if (key == NULL) { + goto done; + } + + path = normalize_reg_path(mem_ctx, key); + if (path == NULL) { + DEBUG(0, ("out of memory! (talloc failed)\n")); + goto done; + } + + if (*path == '\0') { + goto done; + } + + ret = (strrchr(path, '/') == NULL); + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + + +/** + * Check for the existence of a key. + * + * Existence of a key is authoritatively defined by its + * existence in the list of subkeys of its parent key. + * The exeption of this are keys without a parent key, + * i.e. the "base" keys (HKLM, HKCU, ...). + */ +static bool regdb_key_exists(const char *key) +{ + TALLOC_CTX *mem_ctx = talloc_stackframe(); + TDB_DATA value; + bool ret = false; + char *path, *p; + + if (key == NULL) { + goto done; + } + + path = normalize_reg_path(mem_ctx, key); + if (path == NULL) { + DEBUG(0, ("out of memory! (talloc failed)\n")); + goto done; + } + + if (*path == '\0') { + goto done; + } + + p = strrchr(path, '/'); + if (p == NULL) { + /* this is a base key */ + value = regdb_fetch_key_internal(mem_ctx, path); + ret = (value.dptr != NULL); + } else { + /* get the list of subkeys of the parent key */ + uint32 num_items, len, i; + fstring subkeyname; + + *p = '\0'; + p++; + value = regdb_fetch_key_internal(mem_ctx, path); + if (value.dptr == NULL) { + goto done; + } + + len = tdb_unpack(value.dptr, value.dsize, "d", &num_items); + for (i = 0; i < num_items; i++) { + len += tdb_unpack(value.dptr +len, value.dsize -len, + "f", &subkeyname); + if (strequal(subkeyname, p)) { + ret = true; + goto done; + } + } + } + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + + +/*********************************************************************** + Retrieve an array of strings containing subkeys. Memory should be + released by the caller. + ***********************************************************************/ + +int regdb_fetch_keys(const char *key, REGSUBKEY_CTR *ctr) +{ + WERROR werr; + uint32 num_items; + uint8 *buf; + uint32 buflen, len; + int i; + fstring subkeyname; + int ret = -1; + TALLOC_CTX *frame = talloc_stackframe(); + TDB_DATA value; + + DEBUG(11,("regdb_fetch_keys: Enter key => [%s]\n", key ? key : "NULL")); + + if (!regdb_key_exists(key)) { + goto done; + } + + ctr->seqnum = regdb_get_seqnum(); + + value = regdb_fetch_key_internal(frame, key); + + if (value.dptr == NULL) { + DEBUG(10, ("regdb_fetch_keys: no subkeys found for key [%s]\n", + key)); + ret = 0; + goto done; + } + + buf = value.dptr; + buflen = value.dsize; + len = tdb_unpack( buf, buflen, "d", &num_items); + + for (i=0; i<num_items; i++) { + len += tdb_unpack(buf+len, buflen-len, "f", subkeyname); + werr = regsubkey_ctr_addkey(ctr, subkeyname); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(5, ("regdb_fetch_keys: regsubkey_ctr_addkey " + "failed: %s\n", dos_errstr(werr))); + goto done; + } + } + + DEBUG(11,("regdb_fetch_keys: Exit [%d] items\n", num_items)); + + ret = num_items; +done: + TALLOC_FREE(frame); + return ret; +} + +/**************************************************************************** + Unpack a list of registry values frem the TDB + ***************************************************************************/ + +static int regdb_unpack_values(REGVAL_CTR *values, uint8 *buf, int buflen) +{ + int len = 0; + uint32 type; + fstring valuename; + uint32 size; + uint8 *data_p; + uint32 num_values = 0; + int i; + + /* loop and unpack the rest of the registry values */ + + len += tdb_unpack(buf+len, buflen-len, "d", &num_values); + + for ( i=0; i<num_values; i++ ) { + /* unpack the next regval */ + + type = REG_NONE; + size = 0; + data_p = NULL; + valuename[0] = '\0'; + len += tdb_unpack(buf+len, buflen-len, "fdB", + valuename, + &type, + &size, + &data_p); + + /* add the new value. Paranoid protective code -- make sure data_p is valid */ + + if (*valuename && size && data_p) { + regval_ctr_addvalue(values, valuename, type, + (const char *)data_p, size); + } + SAFE_FREE(data_p); /* 'B' option to tdb_unpack does a malloc() */ + + DEBUG(8,("specific: [%s], len: %d\n", valuename, size)); + } + + return len; +} + +/**************************************************************************** + Pack all values in all printer keys + ***************************************************************************/ + +static int regdb_pack_values(REGVAL_CTR *values, uint8 *buf, int buflen) +{ + int len = 0; + int i; + REGISTRY_VALUE *val; + int num_values; + + if ( !values ) + return 0; + + num_values = regval_ctr_numvals( values ); + + /* pack the number of values first */ + + len += tdb_pack( buf+len, buflen-len, "d", num_values ); + + /* loop over all values */ + + for ( i=0; i<num_values; i++ ) { + val = regval_ctr_specific_value( values, i ); + len += tdb_pack(buf+len, buflen-len, "fdB", + regval_name(val), + regval_type(val), + regval_size(val), + regval_data_p(val) ); + } + + return len; +} + +/*********************************************************************** + Retrieve an array of strings containing subkeys. Memory should be + released by the caller. + ***********************************************************************/ + +int regdb_fetch_values( const char* key, REGVAL_CTR *values ) +{ + char *keystr = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + int ret = 0; + TDB_DATA value; + + DEBUG(10,("regdb_fetch_values: Looking for value of key [%s] \n", key)); + + if (!regdb_key_exists(key)) { + goto done; + } + + keystr = talloc_asprintf(ctx, "%s/%s", REG_VALUE_PREFIX, key); + if (!keystr) { + goto done; + } + + values->seqnum = regdb_get_seqnum(); + + value = regdb_fetch_key_internal(ctx, keystr); + + if (!value.dptr) { + /* all keys have zero values by default */ + goto done; + } + + regdb_unpack_values(values, value.dptr, value.dsize); + ret = regval_ctr_numvals(values); + +done: + TALLOC_FREE(ctx); + return ret; +} + +bool regdb_store_values( const char *key, REGVAL_CTR *values ) +{ + TDB_DATA old_data, data; + char *keystr = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + int len; + NTSTATUS status; + bool result = false; + + DEBUG(10,("regdb_store_values: Looking for value of key [%s] \n", key)); + + if (!regdb_key_exists(key)) { + goto done; + } + + ZERO_STRUCT(data); + + len = regdb_pack_values(values, data.dptr, data.dsize); + if (len <= 0) { + DEBUG(0,("regdb_store_values: unable to pack values. len <= 0\n")); + goto done; + } + + data.dptr = TALLOC_ARRAY(ctx, uint8, len); + data.dsize = len; + + len = regdb_pack_values(values, data.dptr, data.dsize); + + SMB_ASSERT( len == data.dsize ); + + keystr = talloc_asprintf(ctx, "%s/%s", REG_VALUE_PREFIX, key ); + if (!keystr) { + goto done; + } + keystr = normalize_reg_path(ctx, keystr); + if (!keystr) { + goto done; + } + + old_data = dbwrap_fetch_bystring(regdb, ctx, keystr); + + if ((old_data.dptr != NULL) + && (old_data.dsize == data.dsize) + && (memcmp(old_data.dptr, data.dptr, data.dsize) == 0)) + { + result = true; + goto done; + } + + status = dbwrap_trans_store_bystring(regdb, keystr, data, TDB_REPLACE); + + result = NT_STATUS_IS_OK(status); + +done: + TALLOC_FREE(ctx); + return result; +} + +static WERROR regdb_get_secdesc(TALLOC_CTX *mem_ctx, const char *key, + struct security_descriptor **psecdesc) +{ + char *tdbkey; + TDB_DATA data; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + WERROR err = WERR_OK; + + DEBUG(10, ("regdb_get_secdesc: Getting secdesc of key [%s]\n", key)); + + if (!regdb_key_exists(key)) { + err = WERR_BADFILE; + goto done; + } + + tdbkey = talloc_asprintf(tmp_ctx, "%s/%s", REG_SECDESC_PREFIX, key); + if (tdbkey == NULL) { + err = WERR_NOMEM; + goto done; + } + normalize_dbkey(tdbkey); + + data = dbwrap_fetch_bystring(regdb, tmp_ctx, tdbkey); + if (data.dptr == NULL) { + err = WERR_BADFILE; + goto done; + } + + status = unmarshall_sec_desc(mem_ctx, (uint8 *)data.dptr, data.dsize, + psecdesc); + + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) { + err = WERR_NOMEM; + } else if (!NT_STATUS_IS_OK(status)) { + err = WERR_REG_CORRUPT; + } + +done: + TALLOC_FREE(tmp_ctx); + return err; +} + +static WERROR regdb_set_secdesc(const char *key, + struct security_descriptor *secdesc) +{ + TALLOC_CTX *mem_ctx = talloc_stackframe(); + char *tdbkey; + NTSTATUS status; + WERROR err = WERR_NOMEM; + TDB_DATA tdbdata; + + if (!regdb_key_exists(key)) { + err = WERR_BADFILE; + goto done; + } + + tdbkey = talloc_asprintf(mem_ctx, "%s/%s", REG_SECDESC_PREFIX, key); + if (tdbkey == NULL) { + goto done; + } + normalize_dbkey(tdbkey); + + if (secdesc == NULL) { + /* assuming a delete */ + status = dbwrap_trans_delete_bystring(regdb, tdbkey); + if (NT_STATUS_IS_OK(status)) { + err = WERR_OK; + } else { + err = ntstatus_to_werror(status); + } + goto done; + } + + err = ntstatus_to_werror(marshall_sec_desc(mem_ctx, secdesc, + &tdbdata.dptr, + &tdbdata.dsize)); + if (!W_ERROR_IS_OK(err)) { + goto done; + } + + status = dbwrap_trans_store_bystring(regdb, tdbkey, tdbdata, 0); + if (!NT_STATUS_IS_OK(status)) { + err = ntstatus_to_werror(status); + goto done; + } + + done: + TALLOC_FREE(mem_ctx); + return err; +} + +bool regdb_subkeys_need_update(REGSUBKEY_CTR *subkeys) +{ + return (regdb_get_seqnum() != subkeys->seqnum); +} + +bool regdb_values_need_update(REGVAL_CTR *values) +{ + return (regdb_get_seqnum() != values->seqnum); +} + +/* + * Table of function pointers for default access + */ + +REGISTRY_OPS regdb_ops = { + .fetch_subkeys = regdb_fetch_keys, + .fetch_values = regdb_fetch_values, + .store_subkeys = regdb_store_keys, + .store_values = regdb_store_values, + .get_secdesc = regdb_get_secdesc, + .set_secdesc = regdb_set_secdesc, + .subkeys_need_update = regdb_subkeys_need_update, + .values_need_update = regdb_values_need_update +}; diff --git a/source3/registry/reg_backend_hkpt_params.c b/source3/registry/reg_backend_hkpt_params.c new file mode 100644 index 0000000000..2ed5e78e1c --- /dev/null +++ b/source3/registry/reg_backend_hkpt_params.c @@ -0,0 +1,70 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * HKPT parameters registry backend. + * + * This replaces the former dynamic hkpt parameters overlay. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +extern REGISTRY_OPS regdb_ops; + +static int hkpt_params_fetch_values(const char *key, REGVAL_CTR *regvals) +{ + uint32 base_index; + uint32 buffer_size; + char *buffer = NULL; + + /* This is ALMOST the same as perflib_009_params, but HKPT has + a "Counters" entry instead of a "Counter" key. <Grrrr> */ + + base_index = reg_perfcount_get_base_index(); + buffer_size = reg_perfcount_get_counter_names(base_index, &buffer); + regval_ctr_addvalue(regvals, "Counters", REG_MULTI_SZ, buffer, + buffer_size); + + if(buffer_size > 0) { + SAFE_FREE(buffer); + } + + buffer_size = reg_perfcount_get_counter_help(base_index, &buffer); + regval_ctr_addvalue(regvals, "Help", REG_MULTI_SZ, buffer, buffer_size); + if(buffer_size > 0) { + SAFE_FREE(buffer); + } + + return regval_ctr_numvals( regvals ); +} + +static int hkpt_params_fetch_subkeys(const char *key, + REGSUBKEY_CTR *subkey_ctr) +{ + return regdb_ops.fetch_subkeys(key, subkey_ctr); +} + +REGISTRY_OPS hkpt_params_reg_ops = { + .fetch_values = hkpt_params_fetch_values, + .fetch_subkeys = hkpt_params_fetch_subkeys, +}; diff --git a/source3/registry/reg_backend_netlogon_params.c b/source3/registry/reg_backend_netlogon_params.c new file mode 100644 index 0000000000..71f88144c8 --- /dev/null +++ b/source3/registry/reg_backend_netlogon_params.c @@ -0,0 +1,57 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Netlogon parameters registry backend. + * + * This replaces the former dynamic netlogon parameters overlay. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +extern REGISTRY_OPS regdb_ops; + +static int netlogon_params_fetch_values(const char *key, REGVAL_CTR *regvals) +{ + uint32 dwValue; + + if (!pdb_get_account_policy(AP_REFUSE_MACHINE_PW_CHANGE, &dwValue)) { + dwValue = 0; + } + + regval_ctr_addvalue(regvals, "RefusePasswordChange", REG_DWORD, + (char*)&dwValue, sizeof(dwValue)); + + return regval_ctr_numvals(regvals); +} + +static int netlogon_params_fetch_subkeys(const char *key, + REGSUBKEY_CTR *subkey_ctr) +{ + return regdb_ops.fetch_subkeys(key, subkey_ctr); +} + +REGISTRY_OPS netlogon_params_reg_ops = { + .fetch_values = netlogon_params_fetch_values, + .fetch_subkeys = netlogon_params_fetch_subkeys, +}; diff --git a/source3/registry/reg_backend_perflib.c b/source3/registry/reg_backend_perflib.c new file mode 100644 index 0000000000..999bca2682 --- /dev/null +++ b/source3/registry/reg_backend_perflib.c @@ -0,0 +1,106 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * perflib registry backend. + * + * This is a virtual overlay, dynamically presenting perflib values. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +extern REGISTRY_OPS regdb_ops; + +#define KEY_PERFLIB_NORM "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION/PERFLIB" +#define KEY_PERFLIB_009_NORM "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION/PERFLIB/009" + +static int perflib_params( REGVAL_CTR *regvals ) +{ + int base_index = -1; + int last_counter = -1; + int last_help = -1; + int version = 0x00010001; + + base_index = reg_perfcount_get_base_index(); + regval_ctr_addvalue(regvals, "Base Index", REG_DWORD, (char *)&base_index, sizeof(base_index)); + last_counter = reg_perfcount_get_last_counter(base_index); + regval_ctr_addvalue(regvals, "Last Counter", REG_DWORD, (char *)&last_counter, sizeof(last_counter)); + last_help = reg_perfcount_get_last_help(last_counter); + regval_ctr_addvalue(regvals, "Last Help", REG_DWORD, (char *)&last_help, sizeof(last_help)); + regval_ctr_addvalue(regvals, "Version", REG_DWORD, (char *)&version, sizeof(version)); + + return regval_ctr_numvals( regvals ); +} + +static int perflib_009_params( REGVAL_CTR *regvals ) +{ + int base_index; + int buffer_size; + char *buffer = NULL; + + base_index = reg_perfcount_get_base_index(); + buffer_size = reg_perfcount_get_counter_names(base_index, &buffer); + regval_ctr_addvalue(regvals, "Counter", REG_MULTI_SZ, buffer, buffer_size); + if(buffer_size > 0) + SAFE_FREE(buffer); + buffer_size = reg_perfcount_get_counter_help(base_index, &buffer); + regval_ctr_addvalue(regvals, "Help", REG_MULTI_SZ, buffer, buffer_size); + if(buffer_size > 0) + SAFE_FREE(buffer); + + return regval_ctr_numvals( regvals ); +} + +static int perflib_fetch_values(const char *key, REGVAL_CTR *regvals) +{ + char *path = NULL; + TALLOC_CTX *ctx = talloc_tos(); + + path = talloc_strdup(ctx, key); + if (path == NULL) { + return -1; + } + path = normalize_reg_path(ctx, path); + if (path == NULL) { + return -1; + } + + if (strncmp(path, KEY_PERFLIB_NORM, strlen(path)) == 0) { + return perflib_params(regvals); + } else if (strncmp(path, KEY_PERFLIB_009_NORM, strlen(path)) == 0) { + return perflib_009_params(regvals); + } else { + return 0; + } +} + +static int perflib_fetch_subkeys(const char *key, + REGSUBKEY_CTR *subkey_ctr) +{ + return regdb_ops.fetch_subkeys(key, subkey_ctr); +} + +REGISTRY_OPS perflib_reg_ops = { + .fetch_values = perflib_fetch_values, + .fetch_subkeys = perflib_fetch_subkeys, +}; diff --git a/source3/registry/reg_backend_printing.c b/source3/registry/reg_backend_printing.c new file mode 100644 index 0000000000..5c1e6eb543 --- /dev/null +++ b/source3/registry/reg_backend_printing.c @@ -0,0 +1,1268 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Implementation of registry virtual views for printing information */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +/* registrt paths used in the print_registry[] */ + +#define KEY_MONITORS "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/MONITORS" +#define KEY_FORMS "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/FORMS" +#define KEY_CONTROL_PRINTERS "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/PRINTERS" +#define KEY_ENVIRONMENTS "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/ENVIRONMENTS" +#define KEY_CONTROL_PRINT "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT" +#define KEY_WINNT_PRINTERS "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION/PRINT/PRINTERS" +#define KEY_WINNT_PRINT "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION/PRINT" +#define KEY_PORTS "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION/PORTS" + +/* callback table for various registry paths below the ones we service in this module */ + +struct reg_dyn_tree { + /* full key path in normalized form */ + const char *path; + + /* callbscks for fetch/store operations */ + int ( *fetch_subkeys) ( const char *path, REGSUBKEY_CTR *subkeys ); + bool (*store_subkeys) ( const char *path, REGSUBKEY_CTR *subkeys ); + int (*fetch_values) ( const char *path, REGVAL_CTR *values ); + bool (*store_values) ( const char *path, REGVAL_CTR *values ); +}; + +/********************************************************************* + ********************************************************************* + ** Utility Functions + ********************************************************************* + *********************************************************************/ + +/*********************************************************************** + simple function to prune a pathname down to the basename of a file + **********************************************************************/ + +static const char *dos_basename(const char *path) +{ + const char *p; + + if (!(p = strrchr( path, '\\'))) { + p = path; + } else { + p++; + } + + return p; +} + +/********************************************************************* + ********************************************************************* + ** "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/FORMS" + ********************************************************************* + *********************************************************************/ + +static int key_forms_fetch_keys(const char *key, REGSUBKEY_CTR *subkeys) +{ + char *p = reg_remaining_path(talloc_tos(), key + strlen(KEY_FORMS)); + + /* no keys below Forms */ + + if (p) { + return -1; + } + + return 0; +} + +/********************************************************************** + *********************************************************************/ + +static int key_forms_fetch_values( const char *key, REGVAL_CTR *values ) +{ + uint32 data[8]; + int i, num_values, form_index = 1; + nt_forms_struct *forms_list = NULL; + nt_forms_struct *form; + + DEBUG(10,("print_values_forms: key=>[%s]\n", key ? key : "NULL" )); + + num_values = get_ntforms( &forms_list ); + + DEBUG(10,("hive_forms_fetch_values: [%d] user defined forms returned\n", + num_values)); + + /* handle user defined forms */ + + for ( i=0; i<num_values; i++ ) { + form = &forms_list[i]; + + data[0] = form->width; + data[1] = form->length; + data[2] = form->left; + data[3] = form->top; + data[4] = form->right; + data[5] = form->bottom; + data[6] = form_index++; + data[7] = form->flag; + + regval_ctr_addvalue( values, form->name, REG_BINARY, (char*)data, sizeof(data) ); + } + + SAFE_FREE( forms_list ); + forms_list = NULL; + + /* handle built-on forms */ + + num_values = get_builtin_ntforms( &forms_list ); + + DEBUG(10,("print_subpath_values_forms: [%d] built-in forms returned\n", + num_values)); + + for ( i=0; i<num_values; i++ ) { + form = &forms_list[i]; + + data[0] = form->width; + data[1] = form->length; + data[2] = form->left; + data[3] = form->top; + data[4] = form->right; + data[5] = form->bottom; + data[6] = form_index++; + data[7] = form->flag; + + regval_ctr_addvalue(values, form->name, REG_BINARY, (char*)data, sizeof(data) ); + } + + SAFE_FREE(forms_list); + + return regval_ctr_numvals(values); +} + +/********************************************************************* + ********************************************************************* + ** "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/PRINTERS" + ** "HKLM/SOFTWARE/MICROSOFT/WINDOWS NT/CURRENTVERSION/PRINT/PRINTERS" + ********************************************************************* + *********************************************************************/ + +/********************************************************************* + strip off prefix for printers key. DOes return a pointer to static + memory. + *********************************************************************/ + +static char *strip_printers_prefix(const char *key) +{ + char *subkeypath = NULL; + char *path = NULL; + TALLOC_CTX *ctx = talloc_tos(); + + path = talloc_strdup(ctx, key); + if (!path) { + return NULL; + } + path = normalize_reg_path(ctx, path); + if (!path) { + return NULL; + } + + /* normalizing the path does not change length, just key delimiters and case */ + + if (strncmp(path, KEY_WINNT_PRINTERS, strlen(KEY_WINNT_PRINTERS)) == 0) { + subkeypath = reg_remaining_path(ctx, key + strlen(KEY_WINNT_PRINTERS)); + } else { + subkeypath = reg_remaining_path(ctx, key + strlen(KEY_CONTROL_PRINTERS)); + } + + TALLOC_FREE(path); + return subkeypath; +} + +/********************************************************************* + *********************************************************************/ + +static int key_printers_fetch_keys( const char *key, REGSUBKEY_CTR *subkeys ) +{ + int n_services = lp_numservices(); + int snum; + fstring sname; + int i; + int num_subkeys = 0; + char *printers_key; + char *printername, *printerdatakey; + NT_PRINTER_INFO_LEVEL *printer = NULL; + fstring *subkey_names = NULL; + + DEBUG(10,("key_printers_fetch_keys: key=>[%s]\n", key ? key : "NULL" )); + + printers_key = strip_printers_prefix( key ); + + if ( !printers_key ) { + /* enumerate all printers */ + + for (snum=0; snum<n_services; snum++) { + if ( !(lp_snum_ok(snum) && lp_print_ok(snum) ) ) + continue; + + /* don't report the [printers] share */ + + if ( strequal( lp_servicename(snum), PRINTERS_NAME ) ) + continue; + + fstrcpy( sname, lp_servicename(snum) ); + + regsubkey_ctr_addkey( subkeys, sname ); + } + + num_subkeys = regsubkey_ctr_numkeys( subkeys ); + goto done; + } + + /* get information for a specific printer */ + + if (!reg_split_path( printers_key, &printername, &printerdatakey )) { + return -1; + } + + /* validate the printer name */ + + for (snum=0; snum<n_services; snum++) { + if ( !lp_snum_ok(snum) || !lp_print_ok(snum) ) + continue; + if (strequal( lp_servicename(snum), printername ) ) + break; + } + + if ( snum>=n_services + || !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, printername) ) ) + { + return -1; + } + + num_subkeys = get_printer_subkeys( printer->info_2->data, printerdatakey?printerdatakey:"", &subkey_names ); + + for ( i=0; i<num_subkeys; i++ ) + regsubkey_ctr_addkey( subkeys, subkey_names[i] ); + + free_a_printer( &printer, 2 ); + + /* no other subkeys below here */ + +done: + SAFE_FREE( subkey_names ); + + return num_subkeys; +} + +/********************************************************************** + Take a list of names and call add_printer_hook() if necessary + Note that we do this a little differently from Windows since the + keyname is the sharename and not the printer name. + *********************************************************************/ + +static bool add_printers_by_registry( REGSUBKEY_CTR *subkeys ) +{ + int i, num_keys, snum; + char *printername; + NT_PRINTER_INFO_LEVEL_2 info2; + NT_PRINTER_INFO_LEVEL printer; + + ZERO_STRUCT( info2 ); + printer.info_2 = &info2; + + num_keys = regsubkey_ctr_numkeys( subkeys ); + + become_root(); + for ( i=0; i<num_keys; i++ ) { + printername = regsubkey_ctr_specific_key( subkeys, i ); + snum = find_service( printername ); + + /* just verify a valied snum for now */ + if ( snum == -1 ) { + fstrcpy( info2.printername, printername ); + fstrcpy( info2.sharename, printername ); + if ( !add_printer_hook(talloc_tos(), NULL, &printer ) ) { + DEBUG(0,("add_printers_by_registry: Failed to add printer [%s]\n", + printername)); + } + } + } + unbecome_root(); + + return True; +} + +/********************************************************************** + *********************************************************************/ + +static bool key_printers_store_keys( const char *key, REGSUBKEY_CTR *subkeys ) +{ + char *printers_key; + char *printername, *printerdatakey; + NT_PRINTER_INFO_LEVEL *printer = NULL; + int i, num_subkeys, num_existing_keys; + char *subkeyname; + fstring *existing_subkeys = NULL; + + printers_key = strip_printers_prefix( key ); + + if ( !printers_key ) { + /* have to deal with some new or deleted printer */ + return add_printers_by_registry( subkeys ); + } + + if (!reg_split_path( printers_key, &printername, &printerdatakey )) { + return False; + } + + /* lookup the printer */ + + if ( !W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, printername)) ) { + DEBUG(0,("key_printers_store_keys: Tried to store subkey for bad printername %s\n", + printername)); + return False; + } + + /* get the top level printer keys */ + + num_existing_keys = get_printer_subkeys( printer->info_2->data, "", &existing_subkeys ); + + for ( i=0; i<num_existing_keys; i++ ) { + + /* remove the key if it has been deleted */ + + if ( !regsubkey_ctr_key_exists( subkeys, existing_subkeys[i] ) ) { + DEBUG(5,("key_printers_store_keys: deleting key %s\n", + existing_subkeys[i])); + delete_printer_key( printer->info_2->data, existing_subkeys[i] ); + } + } + + num_subkeys = regsubkey_ctr_numkeys( subkeys ); + for ( i=0; i<num_subkeys; i++ ) { + subkeyname = regsubkey_ctr_specific_key(subkeys, i); + /* add any missing printer keys */ + if ( lookup_printerkey(printer->info_2->data, subkeyname) == -1 ) { + DEBUG(5,("key_printers_store_keys: adding key %s\n", + existing_subkeys[i])); + if ( add_new_printer_key( printer->info_2->data, subkeyname ) == -1 ) { + SAFE_FREE( existing_subkeys ); + return False; + } + } + } + + /* write back to disk */ + + mod_a_printer( printer, 2 ); + + /* cleanup */ + + free_a_printer( &printer, 2 ); + + SAFE_FREE( existing_subkeys ); + + return True; +} + +/********************************************************************** + *********************************************************************/ + +static void fill_in_printer_values( NT_PRINTER_INFO_LEVEL_2 *info2, REGVAL_CTR *values ) +{ + DEVICEMODE *devmode; + prs_struct prs; + uint32 offset; + UNISTR2 data; + char *p; + uint32 printer_status = PRINTER_STATUS_OK; + + regval_ctr_addvalue( values, "Attributes", REG_DWORD, (char*)&info2->attributes, sizeof(info2->attributes) ); + regval_ctr_addvalue( values, "Priority", REG_DWORD, (char*)&info2->priority, sizeof(info2->attributes) ); + regval_ctr_addvalue( values, "ChangeID", REG_DWORD, (char*)&info2->changeid, sizeof(info2->changeid) ); + regval_ctr_addvalue( values, "Default Priority", REG_DWORD, (char*)&info2->default_priority, sizeof(info2->default_priority) ); + + /* lie and say everything is ok since we don't want to call print_queue_length() to get the real status */ + regval_ctr_addvalue( values, "Status", REG_DWORD, (char*)&printer_status, sizeof(info2->status) ); + + regval_ctr_addvalue( values, "StartTime", REG_DWORD, (char*)&info2->starttime, sizeof(info2->starttime) ); + regval_ctr_addvalue( values, "UntilTime", REG_DWORD, (char*)&info2->untiltime, sizeof(info2->untiltime) ); + + /* strip the \\server\ from this string */ + if ( !(p = strrchr( info2->printername, '\\' ) ) ) + p = info2->printername; + else + p++; + init_unistr2( &data, p, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Name", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + init_unistr2( &data, info2->location, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Location", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + init_unistr2( &data, info2->comment, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Description", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + init_unistr2( &data, info2->parameters, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Parameters", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + init_unistr2( &data, info2->portname, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Port", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + init_unistr2( &data, info2->sharename, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Share Name", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + init_unistr2( &data, info2->drivername, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Printer Driver", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + init_unistr2( &data, info2->sepfile, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Separator File", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + init_unistr2( &data, "WinPrint", UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Print Processor", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + init_unistr2( &data, "RAW", UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Datatype", REG_SZ, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + + /* use a prs_struct for converting the devmode and security + descriptor to REG_BINARY */ + + if (!prs_init( &prs, RPC_MAX_PDU_FRAG_LEN, values, MARSHALL)) + return; + + /* stream the device mode */ + + if ( (devmode = construct_dev_mode( info2->sharename )) != NULL ) { + if ( spoolss_io_devmode( "devmode", &prs, 0, devmode ) ) { + offset = prs_offset( &prs ); + regval_ctr_addvalue( values, "Default Devmode", REG_BINARY, prs_data_p(&prs), offset ); + } + } + + prs_mem_clear( &prs ); + prs_set_offset( &prs, 0 ); + + /* stream the printer security descriptor */ + + if ( info2->secdesc_buf && + info2->secdesc_buf->sd && + info2->secdesc_buf->sd_size ) + { + if ( sec_io_desc("sec_desc", &info2->secdesc_buf->sd, &prs, 0 ) ) { + offset = prs_offset( &prs ); + regval_ctr_addvalue( values, "Security", REG_BINARY, prs_data_p(&prs), offset ); + } + } + + prs_mem_free( &prs ); + + return; +} + +/********************************************************************** + *********************************************************************/ + +static int key_printers_fetch_values( const char *key, REGVAL_CTR *values ) +{ + int num_values; + char *printers_key; + char *printername, *printerdatakey; + NT_PRINTER_INFO_LEVEL *printer = NULL; + NT_PRINTER_DATA *p_data; + int i, key_index; + + printers_key = strip_printers_prefix( key ); + + /* top level key values stored in the registry has no values */ + + if ( !printers_key ) { + /* normalize to the 'HKLM\SOFTWARE\...\Print\Printers' key */ + return regdb_fetch_values( KEY_WINNT_PRINTERS, values ); + } + + /* lookup the printer object */ + + if (!reg_split_path( printers_key, &printername, &printerdatakey )) { + return -1; + } + + if ( !W_ERROR_IS_OK( get_a_printer(NULL, &printer, 2, printername) ) ) + goto done; + + if ( !printerdatakey ) { + fill_in_printer_values( printer->info_2, values ); + goto done; + } + + /* iterate over all printer data keys and fill the regval container */ + + p_data = printer->info_2->data; + if ( (key_index = lookup_printerkey( p_data, printerdatakey )) == -1 ) { + /* failure....should never happen if the client has a valid open handle first */ + DEBUG(10,("key_printers_fetch_values: Unknown keyname [%s]\n", printerdatakey)); + free_a_printer( &printer, 2 ); + return -1; + } + + num_values = regval_ctr_numvals( p_data->keys[key_index].values ); + for ( i=0; i<num_values; i++ ) + regval_ctr_copyvalue( values, regval_ctr_specific_value(p_data->keys[key_index].values, i) ); + + +done: + if ( printer ) + free_a_printer( &printer, 2 ); + + return regval_ctr_numvals( values ); +} + +/********************************************************************** + *********************************************************************/ + +#define REG_IDX_ATTRIBUTES 1 +#define REG_IDX_PRIORITY 2 +#define REG_IDX_DEFAULT_PRIORITY 3 +#define REG_IDX_CHANGEID 4 +#define REG_IDX_STATUS 5 +#define REG_IDX_STARTTIME 6 +#define REG_IDX_NAME 7 +#define REG_IDX_LOCATION 8 +#define REG_IDX_DESCRIPTION 9 +#define REG_IDX_PARAMETERS 10 +#define REG_IDX_PORT 12 +#define REG_IDX_SHARENAME 13 +#define REG_IDX_DRIVER 14 +#define REG_IDX_SEP_FILE 15 +#define REG_IDX_PRINTPROC 16 +#define REG_IDX_DATATYPE 17 +#define REG_IDX_DEVMODE 18 +#define REG_IDX_SECDESC 19 +#define REG_IDX_UNTILTIME 20 + +struct { + const char *name; + int index; +} printer_values_map[] = { + { "Attributes", REG_IDX_ATTRIBUTES }, + { "Priority", REG_IDX_PRIORITY }, + { "Default Priority", REG_IDX_DEFAULT_PRIORITY }, + { "ChangeID", REG_IDX_CHANGEID }, + { "Status", REG_IDX_STATUS }, + { "StartTime", REG_IDX_STARTTIME }, + { "UntilTime", REG_IDX_UNTILTIME }, + { "Name", REG_IDX_NAME }, + { "Location", REG_IDX_LOCATION }, + { "Description", REG_IDX_DESCRIPTION }, + { "Parameters", REG_IDX_PARAMETERS }, + { "Port", REG_IDX_PORT }, + { "Share Name", REG_IDX_SHARENAME }, + { "Printer Driver", REG_IDX_DRIVER }, + { "Separator File", REG_IDX_SEP_FILE }, + { "Print Processor", REG_IDX_PRINTPROC }, + { "Datatype", REG_IDX_DATATYPE }, + { "Default Devmode", REG_IDX_DEVMODE }, + { "Security", REG_IDX_SECDESC }, + { NULL, -1 } +}; + + +static int find_valuename_index( const char *valuename ) +{ + int i; + + for ( i=0; printer_values_map[i].name; i++ ) { + if ( strequal( valuename, printer_values_map[i].name ) ) + return printer_values_map[i].index; + } + + return -1; +} + +/********************************************************************** + *********************************************************************/ + +static void convert_values_to_printer_info_2( NT_PRINTER_INFO_LEVEL_2 *printer2, REGVAL_CTR *values ) +{ + int num_values = regval_ctr_numvals( values ); + uint32 value_index; + REGISTRY_VALUE *val; + int i; + + for ( i=0; i<num_values; i++ ) { + val = regval_ctr_specific_value( values, i ); + value_index = find_valuename_index( regval_name( val ) ); + + switch( value_index ) { + case REG_IDX_ATTRIBUTES: + printer2->attributes = (uint32)(*regval_data_p(val)); + break; + case REG_IDX_PRIORITY: + printer2->priority = (uint32)(*regval_data_p(val)); + break; + case REG_IDX_DEFAULT_PRIORITY: + printer2->default_priority = (uint32)(*regval_data_p(val)); + break; + case REG_IDX_CHANGEID: + printer2->changeid = (uint32)(*regval_data_p(val)); + break; + case REG_IDX_STARTTIME: + printer2->starttime = (uint32)(*regval_data_p(val)); + break; + case REG_IDX_UNTILTIME: + printer2->untiltime = (uint32)(*regval_data_p(val)); + break; + case REG_IDX_NAME: + rpcstr_pull( printer2->printername, regval_data_p(val), sizeof(fstring), regval_size(val), 0 ); + break; + case REG_IDX_LOCATION: + rpcstr_pull( printer2->location, regval_data_p(val), sizeof(fstring), regval_size(val), 0 ); + break; + case REG_IDX_DESCRIPTION: + rpcstr_pull( printer2->comment, regval_data_p(val), sizeof(fstring), regval_size(val), 0 ); + break; + case REG_IDX_PARAMETERS: + rpcstr_pull( printer2->parameters, regval_data_p(val), sizeof(fstring), regval_size(val), 0 ); + break; + case REG_IDX_PORT: + rpcstr_pull( printer2->portname, regval_data_p(val), sizeof(fstring), regval_size(val), 0 ); + break; + case REG_IDX_SHARENAME: + rpcstr_pull( printer2->sharename, regval_data_p(val), sizeof(fstring), regval_size(val), 0 ); + break; + case REG_IDX_DRIVER: + rpcstr_pull( printer2->drivername, regval_data_p(val), sizeof(fstring), regval_size(val), 0 ); + break; + case REG_IDX_SEP_FILE: + rpcstr_pull( printer2->sepfile, regval_data_p(val), sizeof(fstring), regval_size(val), 0 ); + break; + case REG_IDX_PRINTPROC: + rpcstr_pull( printer2->printprocessor, regval_data_p(val), sizeof(fstring), regval_size(val), 0 ); + break; + case REG_IDX_DATATYPE: + rpcstr_pull( printer2->datatype, regval_data_p(val), sizeof(fstring), regval_size(val), 0 ); + break; + case REG_IDX_DEVMODE: + break; + case REG_IDX_SECDESC: + break; + default: + /* unsupported value...throw away */ + DEBUG(8,("convert_values_to_printer_info_2: Unsupported registry value [%s]\n", + regval_name( val ) )); + } + } + + return; +} + +/********************************************************************** + *********************************************************************/ + +static bool key_printers_store_values( const char *key, REGVAL_CTR *values ) +{ + char *printers_key; + char *printername, *keyname; + NT_PRINTER_INFO_LEVEL *printer = NULL; + WERROR result; + + printers_key = strip_printers_prefix( key ); + + /* values in the top level key get stored in the registry */ + + if ( !printers_key ) { + /* normalize on the 'HKLM\SOFTWARE\....\Print\Printers' key */ + return regdb_store_values( KEY_WINNT_PRINTERS, values ); + } + + if (!reg_split_path( printers_key, &printername, &keyname )) { + return False; + } + + if ( !W_ERROR_IS_OK(get_a_printer(NULL, &printer, 2, printername) ) ) + return False; + + /* deal with setting values directly under the printername */ + + if ( !keyname ) { + convert_values_to_printer_info_2( printer->info_2, values ); + } + else { + int num_values = regval_ctr_numvals( values ); + int i; + REGISTRY_VALUE *val; + + delete_printer_key( printer->info_2->data, keyname ); + + /* deal with any subkeys */ + for ( i=0; i<num_values; i++ ) { + val = regval_ctr_specific_value( values, i ); + result = set_printer_dataex( printer, keyname, + regval_name( val ), + regval_type( val ), + regval_data_p( val ), + regval_size( val ) ); + if ( !W_ERROR_IS_OK(result) ) { + DEBUG(0,("key_printers_store_values: failed to set printer data [%s]!\n", + keyname)); + free_a_printer( &printer, 2 ); + return False; + } + } + } + + result = mod_a_printer( printer, 2 ); + + free_a_printer( &printer, 2 ); + + return W_ERROR_IS_OK(result); +} + +/********************************************************************* + ********************************************************************* + ** "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT/ENVIRONMENTS" + ********************************************************************* + *********************************************************************/ + +static int key_driver_fetch_keys( const char *key, REGSUBKEY_CTR *subkeys ) +{ + const char *environments[] = { + "Windows 4.0", + "Windows NT x86", + "Windows NT R4000", + "Windows NT Alpha_AXP", + "Windows NT PowerPC", + "Windows IA64", + "Windows x64", + NULL }; + fstring *drivers = NULL; + int i, env_index, num_drivers; + char *keystr, *base, *subkeypath; + char *key2 = NULL; + int num_subkeys = -1; + int version; + TALLOC_CTX *ctx = talloc_tos(); + + DEBUG(10,("key_driver_fetch_keys key=>[%s]\n", key ? key : "NULL" )); + + keystr = reg_remaining_path(ctx, key + strlen(KEY_ENVIRONMENTS) ); + + /* list all possible architectures */ + + if ( !keystr ) { + for ( num_subkeys=0; environments[num_subkeys]; num_subkeys++ ) + regsubkey_ctr_addkey( subkeys, environments[num_subkeys] ); + + return num_subkeys; + } + + /* we are dealing with a subkey of "Environments */ + key2 = talloc_strdup(ctx, keystr); + if (!key2) { + return -1; + } + keystr = key2; + if (!reg_split_path(keystr, &base, &subkeypath )) { + return -1; + } + + /* sanity check */ + + for ( env_index=0; environments[env_index]; env_index++ ) { + if ( strequal( environments[env_index], base ) ) + break; + } + if ( !environments[env_index] ) + return -1; + + /* ...\Print\Environements\...\ */ + + if ( !subkeypath ) { + regsubkey_ctr_addkey( subkeys, "Drivers" ); + regsubkey_ctr_addkey( subkeys, "Print Processors" ); + + return 2; + } + + /* more of the key path to process */ + + keystr = subkeypath; + if (!reg_split_path( keystr, &base, &subkeypath )) { + return -1; + } + + /* ...\Print\Environements\...\Drivers\ */ + + if ( !subkeypath ) { + if ( strequal(base, "Drivers") ) { + switch ( env_index ) { + case 0: /* Win9x */ + regsubkey_ctr_addkey( subkeys, "Version-0" ); + break; + default: /* Windows NT based systems */ + regsubkey_ctr_addkey( subkeys, "Version-2" ); + regsubkey_ctr_addkey( subkeys, "Version-3" ); + break; + } + + return regsubkey_ctr_numkeys( subkeys ); + } else if ( strequal(base, "Print Processors") ) { + if ( env_index == 1 || env_index == 5 || env_index == 6 ) + + + return regsubkey_ctr_numkeys( subkeys ); + } else + return -1; /* bad path */ + } + + /* we finally get to enumerate the drivers */ + + /* only one possible subkey below PrintProc key */ + + if ( strequal(base, "Print Processors") ) { + keystr = subkeypath; + if (!reg_split_path( keystr, &base, &subkeypath )) { + return -1; + } + + /* no subkeys below this point */ + + if ( subkeypath ) + return -1; + + /* only allow one keyname here -- 'winprint' */ + + return strequal( base, "winprint" ) ? 0 : -1; + } + + /* only dealing with drivers from here on out */ + + keystr = subkeypath; + if (!reg_split_path( keystr, &base, &subkeypath )) { + return -1; + } + + version = atoi(&base[strlen(base)-1]); + + switch (env_index) { + case 0: + if ( version != 0 ) + return -1; + break; + default: + if ( version != 2 && version != 3 ) + return -1; + break; + } + + + if ( !subkeypath ) { + num_drivers = get_ntdrivers( &drivers, environments[env_index], version ); + for ( i=0; i<num_drivers; i++ ) + regsubkey_ctr_addkey( subkeys, drivers[i] ); + + return regsubkey_ctr_numkeys( subkeys ); + } + + /* if anything else left, just say if has no subkeys */ + + DEBUG(1,("key_driver_fetch_keys unhandled key [%s] (subkey == %s\n", + key, subkeypath )); + + return 0; +} + + +/********************************************************************** + *********************************************************************/ + +static void fill_in_driver_values( NT_PRINTER_DRIVER_INFO_LEVEL_3 *info3, REGVAL_CTR *values ) +{ + char *buffer = NULL; + int buffer_size = 0; + int i, length; + const char *filename; + UNISTR2 data; + + filename = dos_basename( info3->driverpath ); + init_unistr2( &data, filename, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Driver", REG_SZ, (char*)data.buffer, + data.uni_str_len*sizeof(uint16) ); + + filename = dos_basename( info3->configfile ); + init_unistr2( &data, filename, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Configuration File", REG_SZ, (char*)data.buffer, + data.uni_str_len*sizeof(uint16) ); + + filename = dos_basename( info3->datafile ); + init_unistr2( &data, filename, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Data File", REG_SZ, (char*)data.buffer, + data.uni_str_len*sizeof(uint16) ); + + filename = dos_basename( info3->helpfile ); + init_unistr2( &data, filename, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Help File", REG_SZ, (char*)data.buffer, + data.uni_str_len*sizeof(uint16) ); + + init_unistr2( &data, info3->defaultdatatype, UNI_STR_TERMINATE); + regval_ctr_addvalue( values, "Data Type", REG_SZ, (char*)data.buffer, + data.uni_str_len*sizeof(uint16) ); + + regval_ctr_addvalue( values, "Version", REG_DWORD, (char*)&info3->cversion, + sizeof(info3->cversion) ); + + if ( info3->dependentfiles ) { + /* place the list of dependent files in a single + character buffer, separating each file name by + a NULL */ + + for ( i=0; strcmp(info3->dependentfiles[i], ""); i++ ) { + /* strip the path to only the file's base name */ + + filename = dos_basename( info3->dependentfiles[i] ); + + length = strlen(filename); + + buffer = (char *)SMB_REALLOC( buffer, buffer_size + (length + 1)*sizeof(uint16) ); + if ( !buffer ) { + break; + } + + init_unistr2( &data, filename, UNI_STR_TERMINATE); + memcpy( buffer+buffer_size, (char*)data.buffer, data.uni_str_len*sizeof(uint16) ); + + buffer_size += (length + 1)*sizeof(uint16); + } + + /* terminated by double NULL. Add the final one here */ + + buffer = (char *)SMB_REALLOC( buffer, buffer_size + 2 ); + if ( !buffer ) { + buffer_size = 0; + } else { + buffer[buffer_size++] = '\0'; + buffer[buffer_size++] = '\0'; + } + } + + regval_ctr_addvalue( values, "Dependent Files", REG_MULTI_SZ, buffer, buffer_size ); + + SAFE_FREE( buffer ); + + return; +} + +/********************************************************************** + *********************************************************************/ + +static int driver_arch_fetch_values( char *key, REGVAL_CTR *values ) +{ + char *keystr, *base, *subkeypath; + fstring arch_environment; + fstring driver; + int version; + NT_PRINTER_DRIVER_INFO_LEVEL driver_ctr; + WERROR w_result; + + if (!reg_split_path( key, &base, &subkeypath )) { + return -1; + } + + /* no values in 'Environments\Drivers\Windows NT x86' */ + + if ( !subkeypath ) + return 0; + + /* We have the Architecture string and some subkey name: + Currently we only support + * Drivers + * Print Processors + Anything else is an error. + */ + + fstrcpy( arch_environment, base ); + + keystr = subkeypath; + if (!reg_split_path( keystr, &base, &subkeypath )) { + return -1; + } + + if ( strequal(base, "Print Processors") ) + return 0; + + /* only Drivers key can be left */ + + if ( !strequal(base, "Drivers") ) + return -1; + + if ( !subkeypath ) + return 0; + + /* We know that we have Architechure\Drivers with some subkey name + The subkey name has to be Version-XX */ + + keystr = subkeypath; + if (!reg_split_path( keystr, &base, &subkeypath )) { + return -1; + } + + if ( !subkeypath ) + return 0; + + version = atoi(&base[strlen(base)-1]); + + /* BEGIN PRINTER DRIVER NAME BLOCK */ + + keystr = subkeypath; + if (!reg_split_path( keystr, &base, &subkeypath )) { + return -1; + } + + /* don't go any deeper for now */ + + fstrcpy( driver, base ); + + w_result = get_a_printer_driver( &driver_ctr, 3, driver, arch_environment, version ); + + if ( !W_ERROR_IS_OK(w_result) ) + return -1; + + fill_in_driver_values( driver_ctr.info_3, values ); + + free_a_printer_driver( driver_ctr, 3 ); + + /* END PRINTER DRIVER NAME BLOCK */ + + + DEBUG(8,("key_driver_fetch_values: Exit\n")); + + return regval_ctr_numvals( values ); +} + +/********************************************************************** + *********************************************************************/ + +static int key_driver_fetch_values( const char *key, REGVAL_CTR *values ) +{ + char *keystr = NULL; + char *subkey = NULL; + TALLOC_CTX *ctx = talloc_tos(); + + DEBUG(8,("key_driver_fetch_values: Enter key => [%s]\n", key ? key : "NULL")); + + /* no values in the Environments key */ + + if (!(keystr = reg_remaining_path(ctx, key + strlen(KEY_ENVIRONMENTS)))) + return 0; + + subkey = talloc_strdup(ctx, keystr); + if (!subkey) { + return 0; + } + + /* pass off to handle subkeys */ + + return driver_arch_fetch_values( subkey, values ); +} + +/********************************************************************* + ********************************************************************* + ** "HKLM/SYSTEM/CURRENTCONTROLSET/CONTROL/PRINT" + ********************************************************************* + *********************************************************************/ + +static int key_print_fetch_keys( const char *key, REGSUBKEY_CTR *subkeys ) +{ + int key_len = strlen(key); + + /* no keys below 'Print' handled here */ + + if ( (key_len != strlen(KEY_CONTROL_PRINT)) && (key_len != strlen(KEY_WINNT_PRINT)) ) + return -1; + + regsubkey_ctr_addkey( subkeys, "Environments" ); + regsubkey_ctr_addkey( subkeys, "Monitors" ); + regsubkey_ctr_addkey( subkeys, "Forms" ); + regsubkey_ctr_addkey( subkeys, "Printers" ); + + return regsubkey_ctr_numkeys( subkeys ); +} + +/********************************************************************** + ********************************************************************* + ** Structure to hold dispatch table of ops for various printer keys. + ** Make sure to always store deeper keys along the same path first so + ** we ge a more specific match. + ********************************************************************* + *********************************************************************/ + +static struct reg_dyn_tree print_registry[] = { +/* just pass the monitor onto the registry tdb */ +{ KEY_MONITORS, + ®db_fetch_keys, + ®db_store_keys, + ®db_fetch_values, + ®db_store_values }, +{ KEY_FORMS, + &key_forms_fetch_keys, + NULL, + &key_forms_fetch_values, + NULL }, +{ KEY_CONTROL_PRINTERS, + &key_printers_fetch_keys, + &key_printers_store_keys, + &key_printers_fetch_values, + &key_printers_store_values }, +{ KEY_ENVIRONMENTS, + &key_driver_fetch_keys, + NULL, + &key_driver_fetch_values, + NULL }, +{ KEY_CONTROL_PRINT, + &key_print_fetch_keys, + NULL, + NULL, + NULL }, +{ KEY_WINNT_PRINTERS, + &key_printers_fetch_keys, + &key_printers_store_keys, + &key_printers_fetch_values, + &key_printers_store_values }, +{ KEY_PORTS, + ®db_fetch_keys, + ®db_store_keys, + ®db_fetch_values, + ®db_store_values }, + +{ NULL, NULL, NULL, NULL, NULL } +}; + + +/********************************************************************** + ********************************************************************* + ** Main reg_printing interface functions + ********************************************************************* + *********************************************************************/ + +/*********************************************************************** + Lookup a key in the print_registry table, returning its index. + -1 on failure + **********************************************************************/ + +static int match_registry_path(const char *key) +{ + int i; + char *path = NULL; + TALLOC_CTX *ctx = talloc_tos(); + + if ( !key ) + return -1; + + path = talloc_strdup(ctx, key); + if (!path) { + return -1; + } + path = normalize_reg_path(ctx, path); + if (!path) { + return -1; + } + + for ( i=0; print_registry[i].path; i++ ) { + if (strncmp( path, print_registry[i].path, strlen(print_registry[i].path) ) == 0 ) + return i; + } + + return -1; +} + +/*********************************************************************** + **********************************************************************/ + +static int regprint_fetch_reg_keys( const char *key, REGSUBKEY_CTR *subkeys ) +{ + int i = match_registry_path( key ); + + if ( i == -1 ) + return -1; + + if ( !print_registry[i].fetch_subkeys ) + return -1; + + return print_registry[i].fetch_subkeys( key, subkeys ); +} + +/********************************************************************** + *********************************************************************/ + +static bool regprint_store_reg_keys( const char *key, REGSUBKEY_CTR *subkeys ) +{ + int i = match_registry_path( key ); + + if ( i == -1 ) + return False; + + if ( !print_registry[i].store_subkeys ) + return False; + + return print_registry[i].store_subkeys( key, subkeys ); +} + +/********************************************************************** + *********************************************************************/ + +static int regprint_fetch_reg_values( const char *key, REGVAL_CTR *values ) +{ + int i = match_registry_path( key ); + + if ( i == -1 ) + return -1; + + /* return 0 values by default since we know the key had + to exist because the client opened a handle */ + + if ( !print_registry[i].fetch_values ) + return 0; + + return print_registry[i].fetch_values( key, values ); +} + +/********************************************************************** + *********************************************************************/ + +static bool regprint_store_reg_values( const char *key, REGVAL_CTR *values ) +{ + int i = match_registry_path( key ); + + if ( i == -1 ) + return False; + + if ( !print_registry[i].store_values ) + return False; + + return print_registry[i].store_values( key, values ); +} + +/* + * Table of function pointers for accessing printing data + */ + +REGISTRY_OPS printing_ops = { + .fetch_subkeys = regprint_fetch_reg_keys, + .fetch_values = regprint_fetch_reg_values, + .store_subkeys = regprint_store_reg_keys, + .store_values = regprint_store_reg_values, +}; diff --git a/source3/registry/reg_backend_prod_options.c b/source3/registry/reg_backend_prod_options.c new file mode 100644 index 0000000000..7ac5c5b4b9 --- /dev/null +++ b/source3/registry/reg_backend_prod_options.c @@ -0,0 +1,70 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Product options registry backend. + * + * This replaces the former dynamic product options overlay. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +extern REGISTRY_OPS regdb_ops; + +static int prod_options_fetch_values(const char *key, REGVAL_CTR *regvals) +{ + const char *value_ascii = ""; + fstring value; + int value_length; + + switch (lp_server_role()) { + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + value_ascii = "LanmanNT"; + break; + case ROLE_STANDALONE: + value_ascii = "ServerNT"; + break; + case ROLE_DOMAIN_MEMBER: + value_ascii = "WinNT"; + break; + } + + value_length = push_ucs2(value, value, value_ascii, sizeof(value), + STR_TERMINATE|STR_NOALIGN ); + regval_ctr_addvalue(regvals, "ProductType", REG_SZ, value, + value_length); + + return regval_ctr_numvals( regvals ); +} + +static int prod_options_fetch_subkeys(const char *key, + REGSUBKEY_CTR *subkey_ctr) +{ + return regdb_ops.fetch_subkeys(key, subkey_ctr); +} + +REGISTRY_OPS prod_options_reg_ops = { + .fetch_values = prod_options_fetch_values, + .fetch_subkeys = prod_options_fetch_subkeys, +}; diff --git a/source3/registry/reg_backend_shares.c b/source3/registry/reg_backend_shares.c new file mode 100644 index 0000000000..ee9e5dc5a1 --- /dev/null +++ b/source3/registry/reg_backend_shares.c @@ -0,0 +1,164 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Implementation of registry virtual views for printing information */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +/********************************************************************** + It is safe to assume that every registry path passed into on of + the exported functions here begins with KEY_PRINTING else + these functions would have never been called. This is a small utility + function to strip the beginning of the path and make a copy that the + caller can modify. Note that the caller is responsible for releasing + the memory allocated here. + **********************************************************************/ + +static char* trim_reg_path( const char *path ) +{ + const char *p; + uint16 key_len = strlen(KEY_SHARES); + + /* + * sanity check...this really should never be True. + * It is only here to prevent us from accessing outside + * the path buffer in the extreme case. + */ + + if ( strlen(path) < key_len ) { + DEBUG(0,("trim_reg_path: Registry path too short! [%s]\n", path)); + return NULL; + } + + + p = path + strlen( KEY_SHARES ); + + if ( *p == '\\' ) + p++; + + if ( *p ) + return SMB_STRDUP(p); + else + return NULL; +} + +/********************************************************************** + Enumerate registry subkey names given a registry path. + Caller is responsible for freeing memory to **subkeys + *********************************************************************/ + +static int shares_subkey_info( const char *key, REGSUBKEY_CTR *subkey_ctr ) +{ + char *path; + bool top_level = False; + int num_subkeys = 0; + + DEBUG(10,("printing_subkey_info: key=>[%s]\n", key)); + + path = trim_reg_path( key ); + + /* check to see if we are dealing with the top level key */ + + if ( !path ) + top_level = True; + + if ( top_level ) { + num_subkeys = 1; + regsubkey_ctr_addkey( subkey_ctr, "Security" ); + } +#if 0 + else + num_subkeys = handle_share_subpath( path, subkey_ctr, NULL ); +#endif + + SAFE_FREE( path ); + + return num_subkeys; +} + +/********************************************************************** + Enumerate registry values given a registry path. + Caller is responsible for freeing memory + *********************************************************************/ + +static int shares_value_info( const char *key, REGVAL_CTR *val ) +{ + char *path; + bool top_level = False; + int num_values = 0; + + DEBUG(10,("printing_value_info: key=>[%s]\n", key)); + + path = trim_reg_path( key ); + + /* check to see if we are dealing with the top level key */ + + if ( !path ) + top_level = True; + + /* fill in values from the getprinterdata_printer_server() */ + if ( top_level ) + num_values = 0; +#if 0 + else + num_values = handle_printing_subpath( path, NULL, val ); +#endif + + SAFE_FREE(path); + + return num_values; +} + +/********************************************************************** + Stub function which always returns failure since we don't want + people storing printing information directly via regostry calls + (for now at least) + *********************************************************************/ + +static bool shares_store_subkey( const char *key, REGSUBKEY_CTR *subkeys ) +{ + return False; +} + +/********************************************************************** + Stub function which always returns failure since we don't want + people storing printing information directly via regostry calls + (for now at least) + *********************************************************************/ + +static bool shares_store_value( const char *key, REGVAL_CTR *val ) +{ + return False; +} + +/* + * Table of function pointers for accessing printing data + */ + +REGISTRY_OPS shares_reg_ops = { + .fetch_subkeys = shares_subkey_info, + .fetch_values = shares_value_info, + .store_subkeys = shares_store_subkey, + .store_values = shares_store_value, +}; + + diff --git a/source3/registry/reg_backend_smbconf.c b/source3/registry/reg_backend_smbconf.c new file mode 100644 index 0000000000..2e4a5f1c1d --- /dev/null +++ b/source3/registry/reg_backend_smbconf.c @@ -0,0 +1,85 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Volker Lendecke 2006 + * Copyright (C) Michael Adam 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +extern REGISTRY_OPS regdb_ops; /* these are the default */ + +static int smbconf_fetch_keys( const char *key, REGSUBKEY_CTR *subkey_ctr ) +{ + return regdb_ops.fetch_subkeys(key, subkey_ctr); +} + +static bool smbconf_store_keys( const char *key, REGSUBKEY_CTR *subkeys ) +{ + return regdb_ops.store_subkeys(key, subkeys); +} + +static int smbconf_fetch_values( const char *key, REGVAL_CTR *val ) +{ + return regdb_ops.fetch_values(key, val); +} + +static bool smbconf_store_values( const char *key, REGVAL_CTR *val ) +{ + return regdb_ops.store_values(key, val); +} + +static bool smbconf_reg_access_check(const char *keyname, uint32 requested, + uint32 *granted, + const struct nt_user_token *token) +{ + if (!(user_has_privileges(token, &se_disk_operators))) { + return False; + } + + *granted = REG_KEY_ALL; + return True; +} + +static WERROR smbconf_get_secdesc(TALLOC_CTX *mem_ctx, const char *key, + struct security_descriptor **psecdesc) +{ + return regdb_ops.get_secdesc(mem_ctx, key, psecdesc); +} + +static WERROR smbconf_set_secdesc(const char *key, + struct security_descriptor *secdesc) +{ + return regdb_ops.set_secdesc(key, secdesc); +} + + +/* + * Table of function pointers for accessing smb.conf data + */ + +REGISTRY_OPS smbconf_reg_ops = { + .fetch_subkeys = smbconf_fetch_keys, + .fetch_values = smbconf_fetch_values, + .store_subkeys = smbconf_store_keys, + .store_values = smbconf_store_values, + .reg_access_check = smbconf_reg_access_check, + .get_secdesc = smbconf_get_secdesc, + .set_secdesc = smbconf_set_secdesc, +}; diff --git a/source3/registry/reg_backend_tcpip_params.c b/source3/registry/reg_backend_tcpip_params.c new file mode 100644 index 0000000000..db7df5dd8f --- /dev/null +++ b/source3/registry/reg_backend_tcpip_params.c @@ -0,0 +1,67 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * TCP/IP parameters registry backend. + * + * This replaces the former dynamic tcpip parameters overlay. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +extern REGISTRY_OPS regdb_ops; + +static int tcpip_params_fetch_values(const char *key, REGVAL_CTR *regvals) +{ + fstring value; + int value_length; + char *hname; + char *mydomainname = NULL; + + hname = myhostname(); + value_length = push_ucs2(value, value, hname, sizeof(value), + STR_TERMINATE|STR_NOALIGN); + regval_ctr_addvalue(regvals, "Hostname",REG_SZ, value, value_length); + + mydomainname = get_mydnsdomname(talloc_tos()); + if (!mydomainname) { + return -1; + } + + value_length = push_ucs2(value, value, mydomainname, sizeof(value), + STR_TERMINATE|STR_NOALIGN); + regval_ctr_addvalue(regvals, "Domain", REG_SZ, value, value_length); + + return regval_ctr_numvals(regvals); +} + +static int tcpip_params_fetch_subkeys(const char *key, + REGSUBKEY_CTR *subkey_ctr) +{ + return regdb_ops.fetch_subkeys(key, subkey_ctr); +} + +REGISTRY_OPS tcpip_params_reg_ops = { + .fetch_values = tcpip_params_fetch_values, + .fetch_subkeys = tcpip_params_fetch_subkeys, +}; diff --git a/source3/registry/reg_cachehook.c b/source3/registry/reg_cachehook.c new file mode 100644 index 0000000000..6697a69356 --- /dev/null +++ b/source3/registry/reg_cachehook.c @@ -0,0 +1,147 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Implementation of registry hook cache tree */ + +#include "includes.h" +#include "adt_tree.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +static SORTED_TREE *cache_tree = NULL; +extern REGISTRY_OPS regdb_ops; /* these are the default */ + +static WERROR keyname_to_path(TALLOC_CTX *mem_ctx, const char *keyname, + char **path) +{ + char *tmp_path = NULL; + + if ((keyname == NULL) || (path == NULL)) { + return WERR_INVALID_PARAM; + } + + tmp_path = talloc_asprintf(mem_ctx, "\\%s", keyname); + if (tmp_path == NULL) { + DEBUG(0, ("talloc_asprintf failed!\n")); + return WERR_NOMEM; + } + + tmp_path = talloc_string_sub(mem_ctx, tmp_path, "\\", "/"); + if (tmp_path == NULL) { + DEBUG(0, ("talloc_string_sub_failed!\n")); + return WERR_NOMEM; + } + + *path = tmp_path; + + return WERR_OK; +} + +/********************************************************************** + Initialize the cache tree if it has not been initialized yet. + *********************************************************************/ + +WERROR reghook_cache_init(void) +{ + if (cache_tree != NULL) { + return WERR_OK; + } + + cache_tree = pathtree_init(®db_ops, NULL); + if (cache_tree == NULL) { + return WERR_NOMEM; + } + DEBUG(10, ("reghook_cache_init: new tree with default " + "ops %p for key [%s]\n", (void *)®db_ops, + KEY_TREE_ROOT)); + return WERR_OK; +} + +/********************************************************************** + Add a new registry hook to the cache. Note that the keyname + is not in the exact format that a SORTED_TREE expects. + *********************************************************************/ + +WERROR reghook_cache_add(const char *keyname, REGISTRY_OPS *ops) +{ + WERROR werr; + char *key = NULL; + + if ((keyname == NULL) || (ops == NULL)) { + return WERR_INVALID_PARAM; + } + + werr = keyname_to_path(talloc_tos(), keyname, &key); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + DEBUG(10, ("reghook_cache_add: Adding ops %p for key [%s]\n", + (void *)ops, key)); + + werr = pathtree_add(cache_tree, key, ops); + +done: + TALLOC_FREE(key); + return werr; +} + +/********************************************************************** + Find a key in the cache. + *********************************************************************/ + +REGISTRY_OPS *reghook_cache_find(const char *keyname) +{ + WERROR werr; + char *key = NULL; + REGISTRY_OPS *ops = NULL; + + if (keyname == NULL) { + return NULL; + } + + werr = keyname_to_path(talloc_tos(), keyname, &key); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + DEBUG(10,("reghook_cache_find: Searching for keyname [%s]\n", key)); + + ops = (REGISTRY_OPS *)pathtree_find(cache_tree, key); + + DEBUG(10, ("reghook_cache_find: found ops %p for key [%s]\n", + ops ? (void *)ops : 0, key)); + +done: + TALLOC_FREE(key); + + return ops; +} + +/********************************************************************** + Print out the cache tree structure for debugging. + *********************************************************************/ + +void reghook_dump_cache( int debuglevel ) +{ + DEBUG(debuglevel,("reghook_dump_cache: Starting cache dump now...\n")); + + pathtree_print_keys( cache_tree, debuglevel ); +} diff --git a/source3/registry/reg_dispatcher.c b/source3/registry/reg_dispatcher.c new file mode 100644 index 0000000000..c68ecdedeb --- /dev/null +++ b/source3/registry/reg_dispatcher.c @@ -0,0 +1,245 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * Copyright (C) Michael Adam 2006-2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Implementation of registry frontend view functions. + * Functions moved from reg_frontend.c to minimize linker deps. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +static const struct generic_mapping reg_generic_map = + { REG_KEY_READ, REG_KEY_WRITE, REG_KEY_EXECUTE, REG_KEY_ALL }; + +/******************************************************************** +********************************************************************/ + +static WERROR construct_registry_sd(TALLOC_CTX *ctx, SEC_DESC **psd) +{ + SEC_ACE ace[3]; + SEC_ACCESS mask; + size_t i = 0; + SEC_DESC *sd; + SEC_ACL *acl; + size_t sd_size; + + /* basic access for Everyone */ + + init_sec_access(&mask, REG_KEY_READ); + init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, + mask, 0); + + /* Full Access 'BUILTIN\Administrators' */ + + init_sec_access(&mask, REG_KEY_ALL); + init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators, + SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); + + /* Full Access 'NT Authority\System' */ + + init_sec_access(&mask, REG_KEY_ALL ); + init_sec_ace(&ace[i++], &global_sid_System, SEC_ACE_TYPE_ACCESS_ALLOWED, + mask, 0); + + /* create the security descriptor */ + + acl = make_sec_acl(ctx, NT4_ACL_REVISION, i, ace); + if (acl == NULL) { + return WERR_NOMEM; + } + + sd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, + &global_sid_Builtin_Administrators, + &global_sid_System, NULL, acl, + &sd_size); + if (sd == NULL) { + return WERR_NOMEM; + } + + *psd = sd; + return WERR_OK; +} + +/*********************************************************************** + High level wrapper function for storing registry subkeys + ***********************************************************************/ + +bool store_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkeys ) +{ + if (key->ops && key->ops->store_subkeys) + return key->ops->store_subkeys(key->name, subkeys); + + return false; +} + +/*********************************************************************** + High level wrapper function for storing registry values + ***********************************************************************/ + +bool store_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val ) +{ + if (key->ops && key->ops->store_values) + return key->ops->store_values(key->name, val); + + return false; +} + +/*********************************************************************** + High level wrapper function for enumerating registry subkeys + Initialize the TALLOC_CTX if necessary + ***********************************************************************/ + +int fetch_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkey_ctr ) +{ + int result = -1; + + if (key->ops && key->ops->fetch_subkeys) + result = key->ops->fetch_subkeys(key->name, subkey_ctr); + + return result; +} + +/*********************************************************************** + High level wrapper function for enumerating registry values + ***********************************************************************/ + +int fetch_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val ) +{ + int result = -1; + + DEBUG(10, ("fetch_reg_values called for key '%s' (ops %p)\n", key->name, + (key->ops) ? (void *)key->ops : NULL)); + + if (key->ops && key->ops->fetch_values) + result = key->ops->fetch_values(key->name, val); + + return result; +} + +/*********************************************************************** + High level access check for passing the required access mask to the + underlying registry backend + ***********************************************************************/ + +bool regkey_access_check( REGISTRY_KEY *key, uint32 requested, uint32 *granted, + const struct nt_user_token *token ) +{ + SEC_DESC *sec_desc; + NTSTATUS status; + WERROR err; + TALLOC_CTX *mem_ctx; + + /* use the default security check if the backend has not defined its + * own */ + + if (key->ops && key->ops->reg_access_check) { + return key->ops->reg_access_check(key->name, requested, + granted, token); + } + + /* + * The secdesc routines can't yet cope with a NULL talloc ctx sanely. + */ + + if (!(mem_ctx = talloc_init("regkey_access_check"))) { + return false; + } + + err = regkey_get_secdesc(mem_ctx, key, &sec_desc); + + if (!W_ERROR_IS_OK(err)) { + TALLOC_FREE(mem_ctx); + return false; + } + + se_map_generic( &requested, ®_generic_map ); + + if (!se_access_check(sec_desc, token, requested, granted, &status)) { + TALLOC_FREE(mem_ctx); + return false; + } + + TALLOC_FREE(mem_ctx); + return NT_STATUS_IS_OK(status); +} + +WERROR regkey_get_secdesc(TALLOC_CTX *mem_ctx, REGISTRY_KEY *key, + struct security_descriptor **psecdesc) +{ + struct security_descriptor *secdesc; + WERROR werr; + + if (key->ops && key->ops->get_secdesc) { + werr = key->ops->get_secdesc(mem_ctx, key->name, psecdesc); + if (W_ERROR_IS_OK(werr)) { + return WERR_OK; + } + } + + werr = construct_registry_sd(mem_ctx, &secdesc); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + *psecdesc = secdesc; + return WERR_OK; +} + +WERROR regkey_set_secdesc(REGISTRY_KEY *key, + struct security_descriptor *psecdesc) +{ + if (key->ops && key->ops->set_secdesc) { + return key->ops->set_secdesc(key->name, psecdesc); + } + + return WERR_ACCESS_DENIED; +} + +/** + * Check whether the in-memory version of the subkyes of a + * registry key needs update from disk. + */ +bool reg_subkeys_need_update(REGISTRY_KEY *key, REGSUBKEY_CTR *subkeys) +{ + if (key->ops && key->ops->subkeys_need_update) + { + return key->ops->subkeys_need_update(subkeys); + } + + return false; +} + +/** + * Check whether the in-memory version of the values of a + * registry key needs update from disk. + */ +bool reg_values_need_update(REGISTRY_KEY *key, REGVAL_CTR *values) +{ + if (key->ops && key->ops->values_need_update) + { + return key->ops->values_need_update(values); + } + + return false; +} + diff --git a/source3/registry/reg_eventlog.c b/source3/registry/reg_eventlog.c new file mode 100644 index 0000000000..8994acf107 --- /dev/null +++ b/source3/registry/reg_eventlog.c @@ -0,0 +1,380 @@ + +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Marcin Krzysztof Porwit 2005, + * Copyright (C) Brian Moran 2005. + * Copyright (C) Gerald (Jerry) Carter 2005. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +/********************************************************************** + for an eventlog, add in the default values +*********************************************************************/ + +bool eventlog_init_keys(void) +{ + /* Find all of the eventlogs, add keys for each of them */ + const char **elogs = lp_eventlog_list(); + char *evtlogpath = NULL; + char *evtfilepath = NULL; + REGSUBKEY_CTR *subkeys; + REGVAL_CTR *values; + uint32 uiMaxSize; + uint32 uiRetention; + uint32 uiCategoryCount; + UNISTR2 data; + TALLOC_CTX *ctx = talloc_tos(); + + while (elogs && *elogs) { + if (!(subkeys = TALLOC_ZERO_P(ctx, REGSUBKEY_CTR ) ) ) { + DEBUG( 0, ( "talloc() failure!\n" ) ); + return False; + } + regdb_fetch_keys(KEY_EVENTLOG, subkeys); + regsubkey_ctr_addkey( subkeys, *elogs ); + if ( !regdb_store_keys( KEY_EVENTLOG, subkeys ) ) { + TALLOC_FREE(subkeys); + return False; + } + TALLOC_FREE(subkeys); + + /* add in the key of form KEY_EVENTLOG/Application */ + DEBUG( 5, + ( "Adding key of [%s] to path of [%s]\n", *elogs, + KEY_EVENTLOG ) ); + + evtlogpath = talloc_asprintf(ctx, "%s\\%s", + KEY_EVENTLOG, *elogs); + if (!evtlogpath) { + return false; + } + /* add in the key of form KEY_EVENTLOG/Application/Application */ + DEBUG( 5, + ( "Adding key of [%s] to path of [%s]\n", *elogs, + evtlogpath ) ); + if (!(subkeys = TALLOC_ZERO_P(ctx, REGSUBKEY_CTR))) { + DEBUG( 0, ( "talloc() failure!\n" ) ); + return False; + } + regdb_fetch_keys( evtlogpath, subkeys ); + regsubkey_ctr_addkey( subkeys, *elogs ); + + if ( !regdb_store_keys( evtlogpath, subkeys ) ) { + TALLOC_FREE(subkeys); + return False; + } + TALLOC_FREE( subkeys ); + + /* now add the values to the KEY_EVENTLOG/Application form key */ + if (!(values = TALLOC_ZERO_P(ctx, REGVAL_CTR))) { + DEBUG( 0, ( "talloc() failure!\n" ) ); + return False; + } + DEBUG( 5, + ( "Storing values to eventlog path of [%s]\n", + evtlogpath ) ); + regdb_fetch_values( evtlogpath, values ); + + + if (!regval_ctr_key_exists(values, "MaxSize")) { + + /* assume we have none, add them all */ + + /* hard code some initial values */ + + /* uiDisplayNameId = 0x00000100; */ + uiMaxSize = 0x00080000; + uiRetention = 0x93A80; + + regval_ctr_addvalue(values, "MaxSize", REG_DWORD, + (char *)&uiMaxSize, + sizeof(uint32)); + + regval_ctr_addvalue(values, "Retention", REG_DWORD, + (char *)&uiRetention, + sizeof(uint32)); + init_unistr2(&data, *elogs, UNI_STR_TERMINATE); + + regval_ctr_addvalue(values, "PrimaryModule", REG_SZ, + (char *)data.buffer, + data.uni_str_len * + sizeof(uint16)); + init_unistr2(&data, *elogs, UNI_STR_TERMINATE); + + regval_ctr_addvalue(values, "Sources", REG_MULTI_SZ, + (char *)data.buffer, + data.uni_str_len * + sizeof(uint16)); + + evtfilepath = talloc_asprintf(ctx, + "%%SystemRoot%%\\system32\\config\\%s.tdb", + *elogs); + if (!evtfilepath) { + TALLOC_FREE(values); + } + init_unistr2(&data, evtfilepath, UNI_STR_TERMINATE); + regval_ctr_addvalue(values, "File", REG_EXPAND_SZ, (char *)data.buffer, + data.uni_str_len * sizeof(uint16)); + regdb_store_values(evtlogpath, values); + + } + + TALLOC_FREE(values); + + /* now do the values under KEY_EVENTLOG/Application/Application */ + TALLOC_FREE(evtlogpath); + evtlogpath = talloc_asprintf(ctx, "%s\\%s\\%s", + KEY_EVENTLOG, *elogs, *elogs); + if (!evtlogpath) { + return false; + } + if (!(values = TALLOC_ZERO_P(ctx, REGVAL_CTR))) { + DEBUG( 0, ( "talloc() failure!\n" ) ); + return False; + } + DEBUG( 5, + ( "Storing values to eventlog path of [%s]\n", + evtlogpath)); + regdb_fetch_values(evtlogpath, values); + if (!regval_ctr_key_exists( values, "CategoryCount")) { + + /* hard code some initial values */ + + uiCategoryCount = 0x00000007; + regval_ctr_addvalue( values, "CategoryCount", + REG_DWORD, + ( char * ) &uiCategoryCount, + sizeof( uint32 ) ); + init_unistr2( &data, + "%SystemRoot%\\system32\\eventlog.dll", + UNI_STR_TERMINATE ); + + regval_ctr_addvalue( values, "CategoryMessageFile", + REG_EXPAND_SZ, + ( char * ) data.buffer, + data.uni_str_len * + sizeof( uint16 ) ); + regdb_store_values( evtlogpath, values ); + } + TALLOC_FREE(values); + elogs++; + } + + return true; +} + +/********************************************************************* + for an eventlog, add in a source name. If the eventlog doesn't + exist (not in the list) do nothing. If a source for the log + already exists, change the information (remove, replace) +*********************************************************************/ + +bool eventlog_add_source( const char *eventlog, const char *sourcename, + const char *messagefile ) +{ + /* Find all of the eventlogs, add keys for each of them */ + /* need to add to the value KEY_EVENTLOG/<eventlog>/Sources string (Creating if necessary) + need to add KEY of source to KEY_EVENTLOG/<eventlog>/<source> */ + + const char **elogs = lp_eventlog_list( ); + char **wrklist, **wp; + char *evtlogpath = NULL; + REGSUBKEY_CTR *subkeys; + REGVAL_CTR *values; + REGISTRY_VALUE *rval; + UNISTR2 data; + uint16 *msz_wp; + int mbytes, ii; + bool already_in; + int i; + int numsources; + TALLOC_CTX *ctx = talloc_tos(); + + if (!elogs) { + return False; + } + + for ( i = 0; elogs[i]; i++ ) { + if ( strequal( elogs[i], eventlog ) ) + break; + } + + if ( !elogs[i] ) { + DEBUG( 0, + ( "Eventlog [%s] not found in list of valid event logs\n", + eventlog ) ); + return false; /* invalid named passed in */ + } + + /* have to assume that the evenlog key itself exists at this point */ + /* add in a key of [sourcename] under the eventlog key */ + + /* todo add to Sources */ + + if (!( values = TALLOC_ZERO_P(ctx, REGVAL_CTR))) { + DEBUG( 0, ( "talloc() failure!\n" )); + return false; + } + + evtlogpath = talloc_asprintf(ctx, "%s\\%s", KEY_EVENTLOG, eventlog); + if (!evtlogpath) { + TALLOC_FREE(values); + return false; + } + + regdb_fetch_values( evtlogpath, values ); + + + if ( !( rval = regval_ctr_getvalue( values, "Sources" ) ) ) { + DEBUG( 0, ( "No Sources value for [%s]!\n", eventlog ) ); + return False; + } + /* perhaps this adding a new string to a multi_sz should be a fn? */ + /* check to see if it's there already */ + + if ( rval->type != REG_MULTI_SZ ) { + DEBUG( 0, + ( "Wrong type for Sources, should be REG_MULTI_SZ\n" ) ); + return False; + } + /* convert to a 'regulah' chars to do some comparisons */ + + already_in = False; + wrklist = NULL; + dump_data( 1, rval->data_p, rval->size ); + if ( ( numsources = + regval_convert_multi_sz( ( uint16 * ) rval->data_p, rval->size, + &wrklist ) ) > 0 ) { + + ii = numsources; + /* see if it's in there already */ + wp = wrklist; + + while ( ii && wp && *wp ) { + if ( strequal( *wp, sourcename ) ) { + DEBUG( 5, + ( "Source name [%s] already in list for [%s] \n", + sourcename, eventlog ) ); + already_in = True; + break; + } + wp++; + ii--; + } + } else { + if ( numsources < 0 ) { + DEBUG( 3, ( "problem in getting the sources\n" ) ); + return False; + } + DEBUG( 3, + ( "Nothing in the sources list, this might be a problem\n" ) ); + } + + wp = wrklist; + + if ( !already_in ) { + /* make a new list with an additional entry; copy values, add another */ + wp = TALLOC_ARRAY(ctx, char *, numsources + 2 ); + + if ( !wp ) { + DEBUG( 0, ( "talloc() failed \n" ) ); + return False; + } + memcpy( wp, wrklist, sizeof( char * ) * numsources ); + *( wp + numsources ) = ( char * ) sourcename; + *( wp + numsources + 1 ) = NULL; + mbytes = regval_build_multi_sz( wp, &msz_wp ); + dump_data( 1, ( uint8 * ) msz_wp, mbytes ); + regval_ctr_addvalue( values, "Sources", REG_MULTI_SZ, + ( char * ) msz_wp, mbytes ); + regdb_store_values( evtlogpath, values ); + TALLOC_FREE(msz_wp); + } else { + DEBUG( 3, + ( "Source name [%s] found in existing list of sources\n", + sourcename ) ); + } + TALLOC_FREE(values); + TALLOC_FREE(wrklist); /* */ + + if ( !( subkeys = TALLOC_ZERO_P(ctx, REGSUBKEY_CTR ) ) ) { + DEBUG( 0, ( "talloc() failure!\n" ) ); + return False; + } + TALLOC_FREE(evtlogpath); + evtlogpath = talloc_asprintf(ctx, "%s\\%s", KEY_EVENTLOG, eventlog ); + if (!evtlogpath) { + TALLOC_FREE(subkeys); + return false; + } + + regdb_fetch_keys( evtlogpath, subkeys ); + + if ( !regsubkey_ctr_key_exists( subkeys, sourcename ) ) { + DEBUG( 5, + ( " Source name [%s] for eventlog [%s] didn't exist, adding \n", + sourcename, eventlog ) ); + regsubkey_ctr_addkey( subkeys, sourcename ); + if ( !regdb_store_keys( evtlogpath, subkeys ) ) + return False; + } + TALLOC_FREE(subkeys); + + /* at this point KEY_EVENTLOG/<eventlog>/<sourcename> key is in there. Now need to add EventMessageFile */ + + /* now allocate room for the source's subkeys */ + + if ( !( subkeys = TALLOC_ZERO_P(ctx, REGSUBKEY_CTR ) ) ) { + DEBUG( 0, ( "talloc() failure!\n" ) ); + return False; + } + TALLOC_FREE(evtlogpath); + evtlogpath = talloc_asprintf(ctx, "%s\\%s\\%s", + KEY_EVENTLOG, eventlog, sourcename); + if (!evtlogpath) { + TALLOC_FREE(subkeys); + return false; + } + + regdb_fetch_keys( evtlogpath, subkeys ); + + /* now add the values to the KEY_EVENTLOG/Application form key */ + if ( !( values = TALLOC_ZERO_P(ctx, REGVAL_CTR ) ) ) { + DEBUG( 0, ( "talloc() failure!\n" ) ); + return False; + } + DEBUG( 5, + ( "Storing EventMessageFile [%s] to eventlog path of [%s]\n", + messagefile, evtlogpath ) ); + + regdb_fetch_values( evtlogpath, values ); + + init_unistr2( &data, messagefile, UNI_STR_TERMINATE ); + + regval_ctr_addvalue( values, "EventMessageFile", REG_SZ, + ( char * ) data.buffer, + data.uni_str_len * sizeof( uint16 ) ); + regdb_store_values( evtlogpath, values ); + + TALLOC_FREE(values); + + return True; +} diff --git a/source3/registry/reg_init_basic.c b/source3/registry/reg_init_basic.c new file mode 100644 index 0000000000..c5e2c346b0 --- /dev/null +++ b/source3/registry/reg_init_basic.c @@ -0,0 +1,55 @@ +/* + * Unix SMB/CIFS implementation. + * Registry helper routines + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +WERROR registry_init_common(void) +{ + WERROR werr; + + werr = regdb_init(); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(0, ("Failed to initialize the registry: %s\n", + dos_errstr(werr))); + goto done; + } + + werr = reghook_cache_init(); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(0, ("Failed to initialize the reghook cache: %s\n", + dos_errstr(werr))); + } + +done: + return werr; +} + +WERROR registry_init_basic(void) +{ + WERROR werr; + + DEBUG(10, ("registry_init_basic called\n")); + + werr = registry_init_common(); + regdb_close(); + return werr; +} diff --git a/source3/registry/reg_init_full.c b/source3/registry/reg_init_full.c new file mode 100644 index 0000000000..91e55d76b2 --- /dev/null +++ b/source3/registry/reg_init_full.c @@ -0,0 +1,105 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Initialize the registry with all available backends. */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +extern REGISTRY_OPS printing_ops; +extern REGISTRY_OPS eventlog_ops; +extern REGISTRY_OPS shares_reg_ops; +extern REGISTRY_OPS smbconf_reg_ops; +extern REGISTRY_OPS netlogon_params_reg_ops; +extern REGISTRY_OPS prod_options_reg_ops; +extern REGISTRY_OPS tcpip_params_reg_ops; +extern REGISTRY_OPS hkpt_params_reg_ops; +extern REGISTRY_OPS current_version_reg_ops; +extern REGISTRY_OPS perflib_reg_ops; +extern REGISTRY_OPS regdb_ops; /* these are the default */ + +/* array of REGISTRY_HOOK's which are read into a tree for easy access */ +/* #define REG_TDB_ONLY 1 */ + +REGISTRY_HOOK reg_hooks[] = { +#ifndef REG_TDB_ONLY + { KEY_PRINTING, &printing_ops }, + { KEY_PRINTING_2K, &printing_ops }, + { KEY_PRINTING_PORTS, &printing_ops }, + { KEY_SHARES, &shares_reg_ops }, + { KEY_SMBCONF, &smbconf_reg_ops }, + { KEY_NETLOGON_PARAMS, &netlogon_params_reg_ops }, + { KEY_PROD_OPTIONS, &prod_options_reg_ops }, + { KEY_TCPIP_PARAMS, &tcpip_params_reg_ops }, + { KEY_HKPT, &hkpt_params_reg_ops }, + { KEY_CURRENT_VERSION, ¤t_version_reg_ops }, + { KEY_PERFLIB, &perflib_reg_ops }, +#endif + { NULL, NULL } +}; + +/*********************************************************************** + Open the registry database and initialize the REGISTRY_HOOK cache + with all available backens. + ***********************************************************************/ + +WERROR registry_init_full(void) +{ + int i; + WERROR werr; + + werr = registry_init_common(); + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + + /* setup the necessary keys and values */ + + werr = init_registry_data(); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(0, ("Failed to initialize data in registry!\n")); + goto fail; + } + + /* build the cache tree of registry hooks */ + + for ( i=0; reg_hooks[i].keyname; i++ ) { + werr = reghook_cache_add(reg_hooks[i].keyname, reg_hooks[i].ops); + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + } + + if ( DEBUGLEVEL >= 20 ) + reghook_dump_cache(20); + + /* add any keys for other services */ + + svcctl_init_keys(); + eventlog_init_keys(); + perfcount_init_keys(); + +fail: + /* close and let each smbd open up as necessary */ + regdb_close(); + return werr; +} diff --git a/source3/registry/reg_init_smbconf.c b/source3/registry/reg_init_smbconf.c new file mode 100644 index 0000000000..43a5be025d --- /dev/null +++ b/source3/registry/reg_init_smbconf.c @@ -0,0 +1,104 @@ +/* + * Unix SMB/CIFS implementation. + * Registry helper routines + * Copyright (C) Michael Adam 2007 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +extern REGISTRY_OPS smbconf_reg_ops; + +/* + * create a fake token just with enough rights to + * locally access the registry: + * + * - builtin administrators sid + * - disk operators privilege + */ +NTSTATUS registry_create_admin_token(TALLOC_CTX *mem_ctx, + NT_USER_TOKEN **ptoken) +{ + NTSTATUS status; + NT_USER_TOKEN *token = NULL; + + if (ptoken == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + token = TALLOC_ZERO_P(mem_ctx, NT_USER_TOKEN); + if (token == NULL) { + DEBUG(1, ("talloc failed\n")); + status = NT_STATUS_NO_MEMORY; + goto done; + } + token->privileges = se_disk_operators; + status = add_sid_to_array(token, &global_sid_Builtin_Administrators, + &token->user_sids, &token->num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Error adding builtin administrators sid " + "to fake token.\n")); + goto done; + } + + *ptoken = token; + +done: + return status; +} + +/* + * init the smbconf portion of the registry. + * for use in places where not the whole registry is needed, + * e.g. utils/net_conf.c and loadparm.c + */ +WERROR registry_init_smbconf(const char *keyname) +{ + WERROR werr; + + DEBUG(10, ("registry_init_smbconf called\n")); + + if (keyname == NULL) { + DEBUG(10, ("registry_init_smbconf: defaulting to key '%s'\n", + KEY_SMBCONF)); + keyname = KEY_SMBCONF; + } + + werr = registry_init_common(); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = init_registry_key(keyname); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(1, ("Failed to initialize registry key '%s': %s\n", + keyname, dos_errstr(werr))); + goto done; + } + + werr = reghook_cache_add(keyname, &smbconf_reg_ops); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(1, ("Failed to add smbconf reghooks to reghook cache: " + "%s\n", dos_errstr(werr))); + goto done; + } + +done: + regdb_close(); + return werr; +} diff --git a/source3/registry/reg_objects.c b/source3/registry/reg_objects.c new file mode 100644 index 0000000000..47122ccad2 --- /dev/null +++ b/source3/registry/reg_objects.c @@ -0,0 +1,431 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Implementation of registry frontend view functions. */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +/********************************************************************** + + Note that the REGSUB_CTR and REGVAL_CTR objects *must* be talloc()'d + since the methods use the object pointer as the talloc context for + internal private data. + + There is no longer a regXXX_ctr_intit() and regXXX_ctr_destroy() + pair of functions. Simply TALLOC_ZERO_P() and TALLOC_FREE() the + object. + + **********************************************************************/ + +/*********************************************************************** + Add a new key to the array + **********************************************************************/ + +WERROR regsubkey_ctr_addkey( REGSUBKEY_CTR *ctr, const char *keyname ) +{ + char **newkeys; + + if ( !keyname ) { + return WERR_OK; + } + + /* make sure the keyname is not already there */ + + if ( regsubkey_ctr_key_exists( ctr, keyname ) ) { + return WERR_OK; + } + + if (!(newkeys = TALLOC_REALLOC_ARRAY(ctr, ctr->subkeys, char *, + ctr->num_subkeys+1))) { + return WERR_NOMEM; + } + + ctr->subkeys = newkeys; + + if (!(ctr->subkeys[ctr->num_subkeys] = talloc_strdup(ctr->subkeys, + keyname ))) { + /* + * Don't shrink the new array again, this wastes a pointer + */ + return WERR_NOMEM; + } + ctr->num_subkeys++; + + return WERR_OK; +} + + /*********************************************************************** + Delete a key from the array + **********************************************************************/ + +int regsubkey_ctr_delkey( REGSUBKEY_CTR *ctr, const char *keyname ) +{ + int i; + + if ( !keyname ) + return ctr->num_subkeys; + + /* make sure the keyname is actually already there */ + + for ( i=0; i<ctr->num_subkeys; i++ ) { + if ( strequal( ctr->subkeys[i], keyname ) ) + break; + } + + if ( i == ctr->num_subkeys ) + return ctr->num_subkeys; + + /* update if we have any keys left */ + ctr->num_subkeys--; + if ( i < ctr->num_subkeys ) + memmove(&ctr->subkeys[i], &ctr->subkeys[i+1], + sizeof(char*) * (ctr->num_subkeys-i)); + + return ctr->num_subkeys; +} + +/*********************************************************************** + Check for the existance of a key + **********************************************************************/ + +bool regsubkey_ctr_key_exists( REGSUBKEY_CTR *ctr, const char *keyname ) +{ + int i; + + if (!ctr->subkeys) { + return False; + } + + for ( i=0; i<ctr->num_subkeys; i++ ) { + if ( strequal( ctr->subkeys[i],keyname ) ) + return True; + } + + return False; +} + +/*********************************************************************** + How many keys does the container hold ? + **********************************************************************/ + +int regsubkey_ctr_numkeys( REGSUBKEY_CTR *ctr ) +{ + return ctr->num_subkeys; +} + +/*********************************************************************** + Retreive a specific key string + **********************************************************************/ + +char* regsubkey_ctr_specific_key( REGSUBKEY_CTR *ctr, uint32 key_index ) +{ + if ( ! (key_index < ctr->num_subkeys) ) + return NULL; + + return ctr->subkeys[key_index]; +} + +/* + * Utility functions for REGVAL_CTR + */ + +/*********************************************************************** + How many keys does the container hold ? + **********************************************************************/ + +int regval_ctr_numvals( REGVAL_CTR *ctr ) +{ + return ctr->num_values; +} + +/*********************************************************************** + allocate memory for and duplicate a REGISTRY_VALUE. + This is malloc'd memory so the caller should free it when done + **********************************************************************/ + +REGISTRY_VALUE* dup_registry_value( REGISTRY_VALUE *val ) +{ + REGISTRY_VALUE *copy = NULL; + + if ( !val ) + return NULL; + + if ( !(copy = SMB_MALLOC_P( REGISTRY_VALUE)) ) { + DEBUG(0,("dup_registry_value: malloc() failed!\n")); + return NULL; + } + + /* copy all the non-pointer initial data */ + + memcpy( copy, val, sizeof(REGISTRY_VALUE) ); + + copy->size = 0; + copy->data_p = NULL; + + if ( val->data_p && val->size ) + { + if ( !(copy->data_p = (uint8 *)memdup( val->data_p, + val->size )) ) { + DEBUG(0,("dup_registry_value: memdup() failed for [%d] " + "bytes!\n", val->size)); + SAFE_FREE( copy ); + return NULL; + } + copy->size = val->size; + } + + return copy; +} + +/********************************************************************** + free the memory allocated to a REGISTRY_VALUE + *********************************************************************/ + +void free_registry_value( REGISTRY_VALUE *val ) +{ + if ( !val ) + return; + + SAFE_FREE( val->data_p ); + SAFE_FREE( val ); + + return; +} + +/********************************************************************** + *********************************************************************/ + +uint8* regval_data_p( REGISTRY_VALUE *val ) +{ + return val->data_p; +} + +/********************************************************************** + *********************************************************************/ + +uint32 regval_size( REGISTRY_VALUE *val ) +{ + return val->size; +} + +/********************************************************************** + *********************************************************************/ + +char* regval_name( REGISTRY_VALUE *val ) +{ + return val->valuename; +} + +/********************************************************************** + *********************************************************************/ + +uint32 regval_type( REGISTRY_VALUE *val ) +{ + return val->type; +} + +/*********************************************************************** + Retreive a pointer to a specific value. Caller shoud dup the structure + since this memory will go away when the ctr is free()'d + **********************************************************************/ + +REGISTRY_VALUE* regval_ctr_specific_value( REGVAL_CTR *ctr, uint32 idx ) +{ + if ( !(idx < ctr->num_values) ) + return NULL; + + return ctr->values[idx]; +} + +/*********************************************************************** + Check for the existance of a value + **********************************************************************/ + +bool regval_ctr_key_exists( REGVAL_CTR *ctr, const char *value ) +{ + int i; + + for ( i=0; i<ctr->num_values; i++ ) { + if ( strequal( ctr->values[i]->valuename, value) ) + return True; + } + + return False; +} + +/*********************************************************************** + * compose a REGISTRY_VALUE from input data + **********************************************************************/ + +REGISTRY_VALUE *regval_compose(TALLOC_CTX *ctx, const char *name, uint16 type, + const char *data_p, size_t size) +{ + REGISTRY_VALUE *regval = TALLOC_P(ctx, REGISTRY_VALUE); + + if (regval == NULL) { + return NULL; + } + + fstrcpy(regval->valuename, name); + regval->type = type; + if (size) { + regval->data_p = (uint8 *)TALLOC_MEMDUP(regval, data_p, size); + if (!regval->data_p) { + TALLOC_FREE(regval); + return NULL; + } + } else { + regval->data_p = NULL; + } + regval->size = size; + + return regval; +} + +/*********************************************************************** + Add a new registry value to the array + **********************************************************************/ + +int regval_ctr_addvalue( REGVAL_CTR *ctr, const char *name, uint16 type, + const char *data_p, size_t size ) +{ + if ( !name ) + return ctr->num_values; + + /* Delete the current value (if it exists) and add the new one */ + + regval_ctr_delvalue( ctr, name ); + + /* allocate a slot in the array of pointers */ + + if ( ctr->num_values == 0 ) { + ctr->values = TALLOC_P( ctr, REGISTRY_VALUE *); + } else { + ctr->values = TALLOC_REALLOC_ARRAY(ctr, ctr->values, + REGISTRY_VALUE *, + ctr->num_values+1); + } + + if (!ctr->values) { + ctr->num_values = 0; + return 0; + } + + /* allocate a new value and store the pointer in the arrya */ + + ctr->values[ctr->num_values] = regval_compose(ctr, name, type, data_p, + size); + if (ctr->values[ctr->num_values] == NULL) { + ctr->num_values = 0; + return 0; + } + ctr->num_values++; + + return ctr->num_values; +} + +/*********************************************************************** + Add a new registry value to the array + **********************************************************************/ + +int regval_ctr_copyvalue( REGVAL_CTR *ctr, REGISTRY_VALUE *val ) +{ + if ( val ) { + regval_ctr_addvalue(ctr, val->valuename, val->type, + (char *)val->data_p, val->size); + } + + return ctr->num_values; +} + +/*********************************************************************** + Delete a single value from the registry container. + No need to free memory since it is talloc'd. + **********************************************************************/ + +int regval_ctr_delvalue( REGVAL_CTR *ctr, const char *name ) +{ + int i; + + for ( i=0; i<ctr->num_values; i++ ) { + if ( strequal( ctr->values[i]->valuename, name ) ) + break; + } + + /* just return if we don't find it */ + + if ( i == ctr->num_values ) + return ctr->num_values; + + /* If 'i' was not the last element, just shift everything down one */ + ctr->num_values--; + if ( i < ctr->num_values ) + memmove(&ctr->values[i], &ctr->values[i+1], + sizeof(REGISTRY_VALUE*)*(ctr->num_values-i)); + + return ctr->num_values; +} + +/*********************************************************************** + Retrieve single value from the registry container. + No need to free memory since it is talloc'd. + **********************************************************************/ + +REGISTRY_VALUE* regval_ctr_getvalue( REGVAL_CTR *ctr, const char *name ) +{ + int i; + + /* search for the value */ + + for ( i=0; i<ctr->num_values; i++ ) { + if ( strequal( ctr->values[i]->valuename, name ) ) + return ctr->values[i]; + } + + return NULL; +} + +/*********************************************************************** + return the data_p as a uint32 + **********************************************************************/ + +uint32 regval_dword( REGISTRY_VALUE *val ) +{ + uint32 data; + + data = IVAL( regval_data_p(val), 0 ); + + return data; +} + +/*********************************************************************** + return the data_p as a character string + **********************************************************************/ + +char *regval_sz(REGISTRY_VALUE *val) +{ + char *data = NULL; + + rpcstr_pull_talloc(talloc_tos(), &data, + regval_data_p(val), regval_size(val),0); + return data; +} diff --git a/source3/registry/reg_perfcount.c b/source3/registry/reg_perfcount.c new file mode 100644 index 0000000000..e608847048 --- /dev/null +++ b/source3/registry/reg_perfcount.c @@ -0,0 +1,1372 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * + * Copyright (C) Marcin Krzysztof Porwit 2005, + * Copyright (C) Gerald (Jerry) Carter 2005. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +#define PERFCOUNT_MAX_LEN 256 + +#define PERFCOUNTDIR "perfmon" +#define NAMES_DB "names.tdb" +#define DATA_DB "data.tdb" + +PERF_OBJECT_TYPE *_reg_perfcount_find_obj(PERF_DATA_BLOCK *block, int objind); + +/********************************************************************* +*********************************************************************/ + +static char *counters_directory(const char *dbname) +{ + char *path = NULL; + char *ret = NULL; + TALLOC_CTX *ctx = talloc_tos(); + + if (!dbname) + return NULL; + + path = talloc_asprintf(ctx, "%s/%s", PERFCOUNTDIR, dbname); + if (!path) { + return NULL; + } + + ret = talloc_strdup(ctx, state_path(path)); + TALLOC_FREE(path); + return ret; +} + +/********************************************************************* +*********************************************************************/ + +void perfcount_init_keys( void ) +{ + char *p = state_path(PERFCOUNTDIR); + + /* no registry keys; just create the perfmon directory */ + + if ( !directory_exist( p, NULL ) ) + mkdir( p, 0755 ); + + return; +} + +/********************************************************************* +*********************************************************************/ + +uint32 reg_perfcount_get_base_index(void) +{ + const char *fname = counters_directory( NAMES_DB ); + TDB_CONTEXT *names; + TDB_DATA kbuf, dbuf; + char key[] = "1"; + uint32 retval = 0; + char buf[PERFCOUNT_MAX_LEN]; + + names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444); + + if ( !names ) { + DEBUG(1, ("reg_perfcount_get_base_index: unable to open [%s].\n", fname)); + return 0; + } + /* needs to read the value of key "1" from the counter_names.tdb file, as that is + where the total number of counters is stored. We're assuming no holes in the + enumeration. + The format for the counter_names.tdb file is: + key value + 1 num_counters + 2 perf_counter1 + 3 perf_counter1_help + 4 perf_counter2 + 5 perf_counter2_help + even_num perf_counter<even_num> + even_num+1 perf_counter<even_num>_help + and so on. + So last_counter becomes num_counters*2, and last_help will be last_counter+1 */ + kbuf = string_tdb_data(key); + dbuf = tdb_fetch(names, kbuf); + if(dbuf.dptr == NULL) + { + DEBUG(1, ("reg_perfcount_get_base_index: failed to find key \'1\' in [%s].\n", fname)); + tdb_close(names); + return 0; + } + else + { + tdb_close(names); + memset(buf, 0, PERFCOUNT_MAX_LEN); + memcpy(buf, dbuf.dptr, dbuf.dsize); + retval = (uint32)atoi(buf); + SAFE_FREE(dbuf.dptr); + return retval; + } + return 0; +} + +/********************************************************************* +*********************************************************************/ + +uint32 reg_perfcount_get_last_counter(uint32 base_index) +{ + uint32 retval; + + if(base_index == 0) + retval = 0; + else + retval = base_index * 2; + + return retval; +} + +/********************************************************************* +*********************************************************************/ + +uint32 reg_perfcount_get_last_help(uint32 last_counter) +{ + uint32 retval; + + if(last_counter == 0) + retval = 0; + else + retval = last_counter + 1; + + return retval; +} + + +/********************************************************************* +*********************************************************************/ + +static uint32 _reg_perfcount_multi_sz_from_tdb(TDB_CONTEXT *tdb, + int keyval, + char **retbuf, + uint32 buffer_size) +{ + TDB_DATA kbuf, dbuf; + char temp[256]; + char *buf1 = *retbuf; + uint32 working_size = 0; + UNISTR2 name_index, name; + + memset(temp, 0, sizeof(temp)); + snprintf(temp, sizeof(temp), "%d", keyval); + kbuf = string_tdb_data(temp); + dbuf = tdb_fetch(tdb, kbuf); + if(dbuf.dptr == NULL) + { + /* If a key isn't there, just bypass it -- this really shouldn't + happen unless someone's mucking around with the tdb */ + DEBUG(3, ("_reg_perfcount_multi_sz_from_tdb: failed to find key [%s] in [%s].\n", + temp, tdb_name(tdb))); + return buffer_size; + } + /* First encode the name_index */ + working_size = (kbuf.dsize + 1)*sizeof(uint16); + buf1 = (char *)SMB_REALLOC(buf1, buffer_size + working_size); + if(!buf1) { + buffer_size = 0; + return buffer_size; + } + init_unistr2(&name_index, (const char *)kbuf.dptr, UNI_STR_TERMINATE); + memcpy(buf1+buffer_size, (char *)name_index.buffer, working_size); + buffer_size += working_size; + /* Now encode the actual name */ + working_size = (dbuf.dsize + 1)*sizeof(uint16); + buf1 = (char *)SMB_REALLOC(buf1, buffer_size + working_size); + if(!buf1) { + buffer_size = 0; + return buffer_size; + } + memset(temp, 0, sizeof(temp)); + memcpy(temp, dbuf.dptr, dbuf.dsize); + SAFE_FREE(dbuf.dptr); + init_unistr2(&name, temp, UNI_STR_TERMINATE); + memcpy(buf1+buffer_size, (char *)name.buffer, working_size); + buffer_size += working_size; + + *retbuf = buf1; + + return buffer_size; +} + +/********************************************************************* +*********************************************************************/ + +uint32 reg_perfcount_get_counter_help(uint32 base_index, char **retbuf) +{ + char *buf1 = NULL; + uint32 buffer_size = 0; + TDB_CONTEXT *names; + const char *fname = counters_directory( NAMES_DB ); + int i; + + if(base_index == 0) + return 0; + + names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444); + + if(names == NULL) + { + DEBUG(1, ("reg_perfcount_get_counter_help: unable to open [%s].\n", fname)); + return 0; + } + + for(i = 1; i <= base_index; i++) + { + buffer_size = _reg_perfcount_multi_sz_from_tdb(names, (i*2)+1, retbuf, buffer_size); + } + tdb_close(names); + + /* Now terminate the MULTI_SZ with a double unicode NULL */ + buf1 = *retbuf; + buf1 = (char *)SMB_REALLOC(buf1, buffer_size + 2); + if(!buf1) { + buffer_size = 0; + } else { + buf1[buffer_size++] = '\0'; + buf1[buffer_size++] = '\0'; + } + + *retbuf = buf1; + + return buffer_size; +} + +/********************************************************************* +*********************************************************************/ + +uint32 reg_perfcount_get_counter_names(uint32 base_index, char **retbuf) +{ + char *buf1 = NULL; + uint32 buffer_size = 0; + TDB_CONTEXT *names; + const char *fname = counters_directory( NAMES_DB ); + int i; + + if(base_index == 0) + return 0; + + names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444); + + if(names == NULL) + { + DEBUG(1, ("reg_perfcount_get_counter_names: unable to open [%s].\n", fname)); + return 0; + } + + buffer_size = _reg_perfcount_multi_sz_from_tdb(names, 1, retbuf, buffer_size); + + for(i = 1; i <= base_index; i++) + { + buffer_size = _reg_perfcount_multi_sz_from_tdb(names, i*2, retbuf, buffer_size); + } + tdb_close(names); + + /* Now terminate the MULTI_SZ with a double unicode NULL */ + buf1 = *retbuf; + buf1 = (char *)SMB_REALLOC(buf1, buffer_size + 2); + if(!buf1) { + buffer_size = 0; + } else { + buf1[buffer_size++] = '\0'; + buf1[buffer_size++] = '\0'; + } + + *retbuf=buf1; + + return buffer_size; +} + +/********************************************************************* +*********************************************************************/ + +static void _reg_perfcount_make_key(TDB_DATA *key, + char *buf, + int buflen, + int key_part1, + const char *key_part2) +{ + memset(buf, 0, buflen); + if(key_part2 != NULL) + snprintf(buf, buflen,"%d%s", key_part1, key_part2); + else + snprintf(buf, buflen, "%d", key_part1); + + *key = string_tdb_data(buf); + + return; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_isparent(TDB_DATA data) +{ + if(data.dsize > 0) + { + if(data.dptr[0] == 'p') + return True; + else + return False; + } + return False; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_ischild(TDB_DATA data) +{ + if(data.dsize > 0) + { + if(data.dptr[0] == 'c') + return True; + else + return False; + } + return False; +} + +/********************************************************************* +*********************************************************************/ + +static uint32 _reg_perfcount_get_numinst(int objInd, TDB_CONTEXT *names) +{ + TDB_DATA key, data; + char buf[PERFCOUNT_MAX_LEN]; + + _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, objInd, "inst"); + data = tdb_fetch(names, key); + + if(data.dptr == NULL) + return (uint32)PERF_NO_INSTANCES; + + memset(buf, 0, PERFCOUNT_MAX_LEN); + memcpy(buf, data.dptr, data.dsize); + SAFE_FREE(data.dptr); + return (uint32)atoi(buf); +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_add_object(PERF_DATA_BLOCK *block, + prs_struct *ps, + int num, + TDB_DATA data, + TDB_CONTEXT *names) +{ + int i; + bool success = True; + PERF_OBJECT_TYPE *obj; + + block->objects = (PERF_OBJECT_TYPE *)TALLOC_REALLOC_ARRAY(ps->mem_ctx, + block->objects, + PERF_OBJECT_TYPE, + block->NumObjectTypes+1); + if(block->objects == NULL) + return False; + obj = &(block->objects[block->NumObjectTypes]); + memset((void *)&(block->objects[block->NumObjectTypes]), 0, sizeof(PERF_OBJECT_TYPE)); + block->objects[block->NumObjectTypes].ObjectNameTitleIndex = num; + block->objects[block->NumObjectTypes].ObjectNameTitlePointer = 0; + block->objects[block->NumObjectTypes].ObjectHelpTitleIndex = num+1; + block->objects[block->NumObjectTypes].ObjectHelpTitlePointer = 0; + block->objects[block->NumObjectTypes].NumCounters = 0; + block->objects[block->NumObjectTypes].DefaultCounter = 0; + block->objects[block->NumObjectTypes].NumInstances = _reg_perfcount_get_numinst(num, names); + block->objects[block->NumObjectTypes].counters = NULL; + block->objects[block->NumObjectTypes].instances = NULL; + block->objects[block->NumObjectTypes].counter_data.ByteLength = sizeof(uint32); + block->objects[block->NumObjectTypes].counter_data.data = NULL; + block->objects[block->NumObjectTypes].DetailLevel = PERF_DETAIL_NOVICE; + block->NumObjectTypes+=1; + + for(i = 0; i < (int)obj->NumInstances; i++) { + success = _reg_perfcount_add_instance(obj, ps, i, names); + } + + return success; +} + +/********************************************************************* +*********************************************************************/ + +bool _reg_perfcount_get_counter_data(TDB_DATA key, TDB_DATA *data) +{ + TDB_CONTEXT *counters; + const char *fname = counters_directory( DATA_DB ); + + counters = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444); + + if(counters == NULL) + { + DEBUG(1, ("reg_perfcount_get_counter_data: unable to open [%s].\n", fname)); + return False; + } + + *data = tdb_fetch(counters, key); + + tdb_close(counters); + + return True; +} + +/********************************************************************* +*********************************************************************/ + +static uint32 _reg_perfcount_get_size_field(uint32 CounterType) +{ + uint32 retval; + + retval = CounterType; + + /* First mask out reserved lower 8 bits */ + retval = retval & 0xFFFFFF00; + retval = retval << 22; + retval = retval >> 22; + + return retval; +} + +/********************************************************************* +*********************************************************************/ + +static uint32 _reg_perfcount_compute_scale(SMB_BIG_INT data) +{ + int scale = 0; + if(data == 0) + return scale; + while(data > 100) + { + data /= 10; + scale--; + } + while(data < 10) + { + data *= 10; + scale++; + } + + return (uint32)scale; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_get_counter_info(PERF_DATA_BLOCK *block, + prs_struct *ps, + int CounterIndex, + PERF_OBJECT_TYPE *obj, + TDB_CONTEXT *names) +{ + TDB_DATA key, data; + char buf[PERFCOUNT_MAX_LEN]; + size_t dsize, padding; + long int data32, dbuf[2]; + SMB_BIG_INT data64; + uint32 counter_size; + + obj->counters[obj->NumCounters].DefaultScale = 0; + dbuf[0] = dbuf[1] = 0; + padding = 0; + + _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, CounterIndex, "type"); + data = tdb_fetch(names, key); + if(data.dptr == NULL) + { + DEBUG(3, ("_reg_perfcount_get_counter_info: No type data for counter [%d].\n", CounterIndex)); + return False; + } + memset(buf, 0, PERFCOUNT_MAX_LEN); + memcpy(buf, data.dptr, data.dsize); + obj->counters[obj->NumCounters].CounterType = atoi(buf); + DEBUG(10, ("_reg_perfcount_get_counter_info: Got type [%d] for counter [%d].\n", + obj->counters[obj->NumCounters].CounterType, CounterIndex)); + SAFE_FREE(data.dptr); + + /* Fetch the actual data */ + _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, CounterIndex, ""); + _reg_perfcount_get_counter_data(key, &data); + if(data.dptr == NULL) + { + DEBUG(3, ("_reg_perfcount_get_counter_info: No counter data for counter [%d].\n", CounterIndex)); + return False; + } + + counter_size = _reg_perfcount_get_size_field(obj->counters[obj->NumCounters].CounterType); + + if(counter_size == PERF_SIZE_DWORD) + { + dsize = sizeof(data32); + memset(buf, 0, PERFCOUNT_MAX_LEN); + memcpy(buf, data.dptr, data.dsize); + data32 = strtol(buf, NULL, 0); + if((obj->counters[obj->NumCounters].CounterType & 0x00000F00) == PERF_TYPE_NUMBER) + obj->counters[obj->NumCounters].DefaultScale = _reg_perfcount_compute_scale((SMB_BIG_INT)data32); + else + obj->counters[obj->NumCounters].DefaultScale = 0; + dbuf[0] = data32; + padding = (dsize - (obj->counter_data.ByteLength%dsize)) % dsize; + } + else if(counter_size == PERF_SIZE_LARGE) + { + dsize = sizeof(data64); + memset(buf, 0, PERFCOUNT_MAX_LEN); + memcpy(buf, data.dptr, data.dsize); + data64 = atof(buf); + if((obj->counters[obj->NumCounters].CounterType & 0x00000F00) == PERF_TYPE_NUMBER) + obj->counters[obj->NumCounters].DefaultScale = _reg_perfcount_compute_scale(data64); + else + obj->counters[obj->NumCounters].DefaultScale = 0; + memcpy((void *)dbuf, (const void *)&data64, dsize); + padding = (dsize - (obj->counter_data.ByteLength%dsize)) % dsize; + } + else /* PERF_SIZE_VARIABLE_LEN */ + { + dsize = data.dsize; + memset(buf, 0, PERFCOUNT_MAX_LEN); + memcpy(buf, data.dptr, data.dsize); + } + SAFE_FREE(data.dptr); + + obj->counter_data.ByteLength += dsize + padding; + obj->counter_data.data = TALLOC_REALLOC_ARRAY(ps->mem_ctx, + obj->counter_data.data, + uint8, + obj->counter_data.ByteLength - sizeof(uint32)); + if(obj->counter_data.data == NULL) + return False; + if(dbuf[0] != 0 || dbuf[1] != 0) + { + memcpy((void *)(obj->counter_data.data + + (obj->counter_data.ByteLength - (sizeof(uint32) + dsize))), + (const void *)dbuf, dsize); + } + else + { + /* Handling PERF_SIZE_VARIABLE_LEN */ + memcpy((void *)(obj->counter_data.data + + (obj->counter_data.ByteLength - (sizeof(uint32) + dsize))), + (const void *)buf, dsize); + } + obj->counters[obj->NumCounters].CounterOffset = obj->counter_data.ByteLength - dsize; + if(obj->counters[obj->NumCounters].CounterOffset % dsize != 0) + { + DEBUG(3,("Improperly aligned counter [%d]\n", obj->NumCounters)); + } + obj->counters[obj->NumCounters].CounterSize = dsize; + + return True; +} + +/********************************************************************* +*********************************************************************/ + +PERF_OBJECT_TYPE *_reg_perfcount_find_obj(PERF_DATA_BLOCK *block, int objind) +{ + int i; + + PERF_OBJECT_TYPE *obj = NULL; + + for(i = 0; i < block->NumObjectTypes; i++) + { + if(block->objects[i].ObjectNameTitleIndex == objind) + { + obj = &(block->objects[i]); + } + } + + return obj; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_add_counter(PERF_DATA_BLOCK *block, + prs_struct *ps, + int num, + TDB_DATA data, + TDB_CONTEXT *names) +{ + char *begin, *end, *start, *stop; + int parent; + PERF_OBJECT_TYPE *obj; + bool success = True; + char buf[PERFCOUNT_MAX_LEN]; + + obj = NULL; + memset(buf, 0, PERFCOUNT_MAX_LEN); + memcpy(buf, data.dptr, data.dsize); + begin = index(buf, '['); + end = index(buf, ']'); + if(begin == NULL || end == NULL) + return False; + start = begin+1; + + while(start < end) { + stop = index(start, ','); + if(stop == NULL) + stop = end; + *stop = '\0'; + parent = atoi(start); + + obj = _reg_perfcount_find_obj(block, parent); + if(obj == NULL) { + /* At this point we require that the parent object exist. + This can probably be handled better at some later time */ + DEBUG(3, ("_reg_perfcount_add_counter: Could not find parent object [%d] for counter [%d].\n", + parent, num)); + return False; + } + obj->counters = (PERF_COUNTER_DEFINITION *)TALLOC_REALLOC_ARRAY(ps->mem_ctx, + obj->counters, + PERF_COUNTER_DEFINITION, + obj->NumCounters+1); + if(obj->counters == NULL) + return False; + memset((void *)&(obj->counters[obj->NumCounters]), 0, sizeof(PERF_COUNTER_DEFINITION)); + obj->counters[obj->NumCounters].CounterNameTitleIndex=num; + obj->counters[obj->NumCounters].CounterHelpTitleIndex=num+1; + obj->counters[obj->NumCounters].DetailLevel = PERF_DETAIL_NOVICE; + obj->counters[obj->NumCounters].ByteLength = sizeof(PERF_COUNTER_DEFINITION); + success = _reg_perfcount_get_counter_info(block, ps, num, obj, names); + obj->NumCounters += 1; + start = stop + 1; + } + + /* Handle case of Objects/Counters without any counter data, which would suggest + that the required instances are not there yet, so change NumInstances from + PERF_NO_INSTANCES to 0 */ + + return success; +} + +/********************************************************************* +*********************************************************************/ + +bool _reg_perfcount_get_instance_info(PERF_INSTANCE_DEFINITION *inst, + prs_struct *ps, + int instId, + PERF_OBJECT_TYPE *obj, + TDB_CONTEXT *names) +{ + TDB_DATA key, data; + char buf[PERFCOUNT_MAX_LEN], temp[PERFCOUNT_MAX_LEN]; + smb_ucs2_t *name = NULL; + int pad; + + /* First grab the instance data from the data file */ + memset(temp, 0, PERFCOUNT_MAX_LEN); + snprintf(temp, PERFCOUNT_MAX_LEN, "i%d", instId); + _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, obj->ObjectNameTitleIndex, temp); + if (!_reg_perfcount_get_counter_data(key, &data)) { + DEBUG(3, ("_reg_perfcount_get_counter_data failed\n")); + return false; + } + if(data.dptr == NULL) + { + DEBUG(3, ("_reg_perfcount_get_instance_info: No instance data for instance [%s].\n", + buf)); + return False; + } + inst->counter_data.ByteLength = data.dsize + sizeof(inst->counter_data.ByteLength); + inst->counter_data.data = TALLOC_REALLOC_ARRAY(ps->mem_ctx, + inst->counter_data.data, + uint8, + data.dsize); + if(inst->counter_data.data == NULL) + return False; + memset(inst->counter_data.data, 0, data.dsize); + memcpy(inst->counter_data.data, data.dptr, data.dsize); + SAFE_FREE(data.dptr); + + /* Fetch instance name */ + memset(temp, 0, PERFCOUNT_MAX_LEN); + snprintf(temp, PERFCOUNT_MAX_LEN, "i%dname", instId); + _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, obj->ObjectNameTitleIndex, temp); + data = tdb_fetch(names, key); + if(data.dptr == NULL) + { + /* Not actually an error, but possibly unintended? -- just logging FYI */ + DEBUG(3, ("_reg_perfcount_get_instance_info: No instance name for instance [%s].\n", + buf)); + inst->NameLength = 0; + } + else + { + memset(buf, 0, PERFCOUNT_MAX_LEN); + memcpy(buf, data.dptr, MIN(PERFCOUNT_MAX_LEN-1,data.dsize)); + buf[PERFCOUNT_MAX_LEN-1] = '\0'; + inst->NameLength = rpcstr_push_talloc(ps->mem_ctx, &name, buf); + if (inst->NameLength == (uint32_t)-1 || !name) { + SAFE_FREE(data.dptr); + return False; + } + inst->data = TALLOC_REALLOC_ARRAY(ps->mem_ctx, + inst->data, + uint8, + inst->NameLength); + if (inst->data == NULL) { + SAFE_FREE(data.dptr); + return False; + } + memcpy(inst->data, name, inst->NameLength); + SAFE_FREE(data.dptr); + } + + inst->ParentObjectTitleIndex = 0; + inst->ParentObjectTitlePointer = 0; + inst->UniqueID = PERF_NO_UNIQUE_ID; + inst->NameOffset = 6 * sizeof(uint32); + + inst->ByteLength = inst->NameOffset + inst->NameLength; + /* Need to be aligned on a 64-bit boundary here for counter_data */ + if((pad = (inst->ByteLength % 8))) + { + pad = 8 - pad; + inst->data = TALLOC_REALLOC_ARRAY(ps->mem_ctx, + inst->data, + uint8, + inst->NameLength + pad); + memset(inst->data + inst->NameLength, 0, pad); + inst->ByteLength += pad; + } + + return True; +} + +/********************************************************************* +*********************************************************************/ + +bool _reg_perfcount_add_instance(PERF_OBJECT_TYPE *obj, + prs_struct *ps, + int instInd, + TDB_CONTEXT *names) +{ + PERF_INSTANCE_DEFINITION *inst; + + if(obj->instances == NULL) { + obj->instances = TALLOC_REALLOC_ARRAY(ps->mem_ctx, + obj->instances, + PERF_INSTANCE_DEFINITION, + obj->NumInstances); + } + if(obj->instances == NULL) + return False; + + memset(&(obj->instances[instInd]), 0, sizeof(PERF_INSTANCE_DEFINITION)); + inst = &(obj->instances[instInd]); + return _reg_perfcount_get_instance_info(inst, ps, instInd, obj, names); +} + +/********************************************************************* +*********************************************************************/ + +static int _reg_perfcount_assemble_global(PERF_DATA_BLOCK *block, + prs_struct *ps, + int base_index, + TDB_CONTEXT *names) +{ + bool success; + int i, j, retval = 0; + char keybuf[PERFCOUNT_MAX_LEN]; + TDB_DATA key, data; + + for(i = 1; i <= base_index; i++) + { + j = i*2; + _reg_perfcount_make_key(&key, keybuf, PERFCOUNT_MAX_LEN, j, "rel"); + data = tdb_fetch(names, key); + if(data.dptr != NULL) + { + if(_reg_perfcount_isparent(data)) + success = _reg_perfcount_add_object(block, ps, j, data, names); + else if(_reg_perfcount_ischild(data)) + success = _reg_perfcount_add_counter(block, ps, j, data, names); + else + { + DEBUG(3, ("Bogus relationship [%s] for counter [%d].\n", data.dptr, j)); + success = False; + } + if(success == False) + { + DEBUG(3, ("_reg_perfcount_assemble_global: Failed to add new relationship for counter [%d].\n", j)); + retval = -1; + } + SAFE_FREE(data.dptr); + } + else + DEBUG(3, ("NULL relationship for counter [%d] using key [%s].\n", j, keybuf)); + } + return retval; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_get_64(SMB_BIG_UINT *retval, + TDB_CONTEXT *tdb, + int key_part1, + const char *key_part2) +{ + TDB_DATA key, data; + char buf[PERFCOUNT_MAX_LEN]; + + _reg_perfcount_make_key(&key, buf, PERFCOUNT_MAX_LEN, key_part1, key_part2); + + data = tdb_fetch(tdb, key); + if(data.dptr == NULL) + { + DEBUG(3,("_reg_perfcount_get_64: No data found for key [%s].\n", key.dptr)); + return False; + } + + memset(buf, 0, PERFCOUNT_MAX_LEN); + memcpy(buf, data.dptr, data.dsize); + SAFE_FREE(data.dptr); + + *retval = atof(buf); + + return True; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_init_data_block_perf(PERF_DATA_BLOCK *block, + TDB_CONTEXT *names) +{ + SMB_BIG_UINT PerfFreq, PerfTime, PerfTime100nSec; + TDB_CONTEXT *counters; + bool status = False; + const char *fname = counters_directory( DATA_DB ); + + counters = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444); + + if(counters == NULL) + { + DEBUG(1, ("reg_perfcount_init_data_block_perf: unable to open [%s].\n", fname)); + return False; + } + + status = _reg_perfcount_get_64(&PerfFreq, names, 0, "PerfFreq"); + if(status == False) + { + tdb_close(counters); + return status; + } + memcpy((void *)&(block->PerfFreq), (const void *)&PerfFreq, sizeof(PerfFreq)); + + status = _reg_perfcount_get_64(&PerfTime, counters, 0, "PerfTime"); + if(status == False) + { + tdb_close(counters); + return status; + } + memcpy((void *)&(block->PerfTime), (const void *)&PerfTime, sizeof(PerfTime)); + + status = _reg_perfcount_get_64(&PerfTime100nSec, counters, 0, "PerfTime100nSec"); + if(status == False) + { + tdb_close(counters); + return status; + } + memcpy((void *)&(block->PerfTime100nSec), (const void *)&PerfTime100nSec, sizeof(PerfTime100nSec)); + + tdb_close(counters); + return True; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_init_data_block(PERF_DATA_BLOCK *block, + prs_struct *ps, TDB_CONTEXT *names) +{ + smb_ucs2_t *temp = NULL; + time_t tm; + + if (rpcstr_push_talloc(ps->mem_ctx, &temp, "PERF")==(size_t)-1) { + return false; + } + if (!temp) { + return false; + } + memcpy(block->Signature, temp, strlen_w(temp) *2); + + if(ps->bigendian_data == RPC_BIG_ENDIAN) + block->LittleEndian = 0; + else + block->LittleEndian = 1; + block->Version = 1; + block->Revision = 1; + block->TotalByteLength = 0; + block->NumObjectTypes = 0; + block->DefaultObject = -1; + block->objects = NULL; + tm = time(NULL); + make_systemtime(&(block->SystemTime), gmtime(&tm)); + _reg_perfcount_init_data_block_perf(block, names); + memset(temp, 0, sizeof(temp)); + rpcstr_push((void *)temp, global_myname(), sizeof(temp), STR_TERMINATE); + block->SystemNameLength = (strlen_w(temp) * 2) + 2; + block->data = TALLOC_ZERO_ARRAY(ps->mem_ctx, uint8, block->SystemNameLength + (8 - (block->SystemNameLength % 8))); + if (block->data == NULL) { + return False; + } + memcpy(block->data, temp, block->SystemNameLength); + block->SystemNameOffset = sizeof(PERF_DATA_BLOCK) - sizeof(block->objects) - sizeof(block->data); + block->HeaderLength = block->SystemNameOffset + block->SystemNameLength; + /* Make sure to adjust for 64-bit alignment for when we finish writing the system name, + so that the PERF_OBJECT_TYPE struct comes out 64-bit aligned */ + block->HeaderLength += 8 - (block->HeaderLength % 8); + + return True; +} + +/********************************************************************* +*********************************************************************/ + +static uint32 _reg_perfcount_perf_data_block_fixup(PERF_DATA_BLOCK *block, prs_struct *ps) +{ + int obj, cnt, inst, pad, i; + PERF_OBJECT_TYPE *object; + PERF_INSTANCE_DEFINITION *instance; + PERF_COUNTER_DEFINITION *counter; + PERF_COUNTER_BLOCK *counter_data; + char *temp = NULL, *src_addr, *dst_addr; + + block->TotalByteLength = 0; + object = block->objects; + for(obj = 0; obj < block->NumObjectTypes; obj++) + { + object[obj].TotalByteLength = 0; + object[obj].DefinitionLength = 0; + instance = object[obj].instances; + counter = object[obj].counters; + for(cnt = 0; cnt < object[obj].NumCounters; cnt++) + { + object[obj].TotalByteLength += counter[cnt].ByteLength; + object[obj].DefinitionLength += counter[cnt].ByteLength; + } + if(object[obj].NumInstances != PERF_NO_INSTANCES) + { + for(inst = 0; inst < object[obj].NumInstances; inst++) + { + instance = &(object[obj].instances[inst]); + object[obj].TotalByteLength += instance->ByteLength; + counter_data = &(instance->counter_data); + counter = &(object[obj].counters[object[obj].NumCounters - 1]); + counter_data->ByteLength = counter->CounterOffset + counter->CounterSize + sizeof(counter_data->ByteLength); + temp = TALLOC_REALLOC_ARRAY(ps->mem_ctx, + temp, + char, + counter_data->ByteLength- sizeof(counter_data->ByteLength)); + if (temp == NULL) { + return 0; + } + memset(temp, 0, counter_data->ByteLength - sizeof(counter_data->ByteLength)); + src_addr = (char *)counter_data->data; + for(i = 0; i < object[obj].NumCounters; i++) + { + counter = &(object[obj].counters[i]); + dst_addr = temp + counter->CounterOffset - sizeof(counter_data->ByteLength); + memcpy(dst_addr, src_addr, counter->CounterSize); + src_addr += counter->CounterSize; + } + /* Make sure to be 64-bit aligned */ + if((pad = (counter_data->ByteLength % 8))) + { + pad = 8 - pad; + } + counter_data->data = TALLOC_REALLOC_ARRAY(ps->mem_ctx, + counter_data->data, + uint8, + counter_data->ByteLength - sizeof(counter_data->ByteLength) + pad); + if (counter_data->data == NULL) { + return 0; + } + memset(counter_data->data, 0, counter_data->ByteLength - sizeof(counter_data->ByteLength) + pad); + memcpy(counter_data->data, temp, counter_data->ByteLength - sizeof(counter_data->ByteLength)); + counter_data->ByteLength += pad; + object[obj].TotalByteLength += counter_data->ByteLength; + } + } + else + { + /* Need to be 64-bit aligned at the end of the counter_data block, so pad counter_data to a 64-bit boundary, + so that the next PERF_OBJECT_TYPE can start on a 64-bit alignment */ + if((pad = (object[obj].counter_data.ByteLength % 8))) + { + pad = 8 - pad; + object[obj].counter_data.data = TALLOC_REALLOC_ARRAY(ps->mem_ctx, + object[obj].counter_data.data, + uint8, + object[obj].counter_data.ByteLength + pad); + memset((void *)(object[obj].counter_data.data + object[obj].counter_data.ByteLength), 0, pad); + object[obj].counter_data.ByteLength += pad; + } + object[obj].TotalByteLength += object[obj].counter_data.ByteLength; + } + object[obj].HeaderLength = sizeof(*object) - (sizeof(counter) + sizeof(instance) + sizeof(PERF_COUNTER_BLOCK)); + object[obj].TotalByteLength += object[obj].HeaderLength; + object[obj].DefinitionLength += object[obj].HeaderLength; + + block->TotalByteLength += object[obj].TotalByteLength; + } + + return block->TotalByteLength; +} + +/********************************************************************* +*********************************************************************/ + +uint32 reg_perfcount_get_perf_data_block(uint32 base_index, + prs_struct *ps, + PERF_DATA_BLOCK *block, + const char *object_ids) +{ + uint32 buffer_size = 0; + const char *fname = counters_directory( NAMES_DB ); + TDB_CONTEXT *names; + int retval = 0; + + names = tdb_open_log(fname, 0, TDB_DEFAULT, O_RDONLY, 0444); + + if(names == NULL) + { + DEBUG(1, ("reg_perfcount_get_perf_data_block: unable to open [%s].\n", fname)); + return 0; + } + + if (!_reg_perfcount_init_data_block(block, ps, names)) { + DEBUG(0, ("_reg_perfcount_init_data_block failed\n")); + tdb_close(names); + return 0; + } + + reg_perfcount_get_last_counter(base_index); + + if(object_ids == NULL) + { + /* we're getting a request for "Global" here */ + retval = _reg_perfcount_assemble_global(block, ps, base_index, names); + } + else + { + /* we're getting a request for a specific set of PERF_OBJECT_TYPES */ + retval = _reg_perfcount_assemble_global(block, ps, base_index, names); + } + buffer_size = _reg_perfcount_perf_data_block_fixup(block, ps); + + tdb_close(names); + + if (retval == -1) { + return 0; + } + + return buffer_size + block->HeaderLength; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_marshall_perf_data_block(prs_struct *ps, PERF_DATA_BLOCK block, int depth) +{ + int i; + prs_debug(ps, depth, "", "_reg_perfcount_marshall_perf_data_block"); + depth++; + + if(!prs_align(ps)) + return False; + for(i = 0; i < 4; i++) + { + if(!prs_uint16("Signature", ps, depth, &block.Signature[i])) + return False; + } + if(!prs_uint32("Little Endian", ps, depth, &block.LittleEndian)) + return False; + if(!prs_uint32("Version", ps, depth, &block.Version)) + return False; + if(!prs_uint32("Revision", ps, depth, &block.Revision)) + return False; + if(!prs_uint32("TotalByteLength", ps, depth, &block.TotalByteLength)) + return False; + if(!prs_uint32("HeaderLength", ps, depth, &block.HeaderLength)) + return False; + if(!prs_uint32("NumObjectTypes", ps, depth, &block.NumObjectTypes)) + return False; + if(!prs_uint32("DefaultObject", ps, depth, &block.DefaultObject)) + return False; + if(!spoolss_io_system_time("SystemTime", ps, depth, &block.SystemTime)) + return False; + if(!prs_uint32("Padding", ps, depth, &block.Padding)) + return False; + if(!prs_align_uint64(ps)) + return False; + if(!prs_uint64("PerfTime", ps, depth, &block.PerfTime)) + return False; + if(!prs_uint64("PerfFreq", ps, depth, &block.PerfFreq)) + return False; + if(!prs_uint64("PerfTime100nSec", ps, depth, &block.PerfTime100nSec)) + return False; + if(!prs_uint32("SystemNameLength", ps, depth, &block.SystemNameLength)) + return False; + if(!prs_uint32("SystemNameOffset", ps, depth, &block.SystemNameOffset)) + return False; + /* hack to make sure we're 64-bit aligned at the end of this whole mess */ + if(!prs_uint8s(False, "SystemName", ps, depth, block.data, + block.HeaderLength - block.SystemNameOffset)) + return False; + + return True; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_marshall_perf_counters(prs_struct *ps, + PERF_OBJECT_TYPE object, + int depth) +{ + int cnt; + PERF_COUNTER_DEFINITION counter; + + prs_debug(ps, depth, "", "_reg_perfcount_marshall_perf_counters"); + depth++; + + for(cnt = 0; cnt < object.NumCounters; cnt++) + { + counter = object.counters[cnt]; + + if(!prs_align(ps)) + return False; + if(!prs_uint32("ByteLength", ps, depth, &counter.ByteLength)) + return False; + if(!prs_uint32("CounterNameTitleIndex", ps, depth, &counter.CounterNameTitleIndex)) + return False; + if(!prs_uint32("CounterNameTitlePointer", ps, depth, &counter.CounterNameTitlePointer)) + return False; + if(!prs_uint32("CounterHelpTitleIndex", ps, depth, &counter.CounterHelpTitleIndex)) + return False; + if(!prs_uint32("CounterHelpTitlePointer", ps, depth, &counter.CounterHelpTitlePointer)) + return False; + if(!prs_uint32("DefaultScale", ps, depth, &counter.DefaultScale)) + return False; + if(!prs_uint32("DetailLevel", ps, depth, &counter.DetailLevel)) + return False; + if(!prs_uint32("CounterType", ps, depth, &counter.CounterType)) + return False; + if(!prs_uint32("CounterSize", ps, depth, &counter.CounterSize)) + return False; + if(!prs_uint32("CounterOffset", ps, depth, &counter.CounterOffset)) + return False; + } + + return True; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_marshall_perf_counter_data(prs_struct *ps, + PERF_COUNTER_BLOCK counter_data, + int depth) +{ + prs_debug(ps, depth, "", "_reg_perfcount_marshall_perf_counter_data"); + depth++; + + if(!prs_align_uint64(ps)) + return False; + + if(!prs_uint32("ByteLength", ps, depth, &counter_data.ByteLength)) + return False; + if(!prs_uint8s(False, "CounterData", ps, depth, counter_data.data, counter_data.ByteLength - sizeof(uint32))) + return False; + if(!prs_align_uint64(ps)) + return False; + + return True; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_marshall_perf_instances(prs_struct *ps, + PERF_OBJECT_TYPE object, + int depth) +{ + PERF_INSTANCE_DEFINITION instance; + int inst; + + prs_debug(ps, depth, "", "_reg_perfcount_marshall_perf_instances"); + depth++; + + for(inst = 0; inst < object.NumInstances; inst++) + { + instance = object.instances[inst]; + + if(!prs_align(ps)) + return False; + if(!prs_uint32("ByteLength", ps, depth, &instance.ByteLength)) + return False; + if(!prs_uint32("ParentObjectTitleIndex", ps, depth, &instance.ParentObjectTitleIndex)) + return False; + if(!prs_uint32("ParentObjectTitlePointer", ps, depth, &instance.ParentObjectTitlePointer)) + return False; + if(!prs_uint32("UniqueID", ps, depth, &instance.UniqueID)) + return False; + if(!prs_uint32("NameOffset", ps, depth, &instance.NameOffset)) + return False; + if(!prs_uint32("NameLength", ps, depth, &instance.NameLength)) + return False; + if(!prs_uint8s(False, "InstanceName", ps, depth, instance.data, + instance.ByteLength - instance.NameOffset)) + return False; + if(_reg_perfcount_marshall_perf_counter_data(ps, instance.counter_data, depth) == False) + return False; + } + + return True; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_marshall_perf_objects(prs_struct *ps, PERF_DATA_BLOCK block, int depth) +{ + int obj; + + PERF_OBJECT_TYPE object; + + prs_debug(ps, depth, "", "_reg_perfcount_marshall_perf_objects"); + depth++; + + for(obj = 0; obj < block.NumObjectTypes; obj++) + { + object = block.objects[obj]; + + if(!prs_align(ps)) + return False; + + if(!prs_uint32("TotalByteLength", ps, depth, &object.TotalByteLength)) + return False; + if(!prs_uint32("DefinitionLength", ps, depth, &object.DefinitionLength)) + return False; + if(!prs_uint32("HeaderLength", ps, depth, &object.HeaderLength)) + return False; + if(!prs_uint32("ObjectNameTitleIndex", ps, depth, &object.ObjectNameTitleIndex)) + return False; + if(!prs_uint32("ObjectNameTitlePointer", ps, depth, &object.ObjectNameTitlePointer)) + return False; + if(!prs_uint32("ObjectHelpTitleIndex", ps, depth, &object.ObjectHelpTitleIndex)) + return False; + if(!prs_uint32("ObjectHelpTitlePointer", ps, depth, &object.ObjectHelpTitlePointer)) + return False; + if(!prs_uint32("DetailLevel", ps, depth, &object.DetailLevel)) + return False; + if(!prs_uint32("NumCounters", ps, depth, &object.NumCounters)) + return False; + if(!prs_uint32("DefaultCounter", ps, depth, &object.DefaultCounter)) + return False; + if(!prs_uint32("NumInstances", ps, depth, &object.NumInstances)) + return False; + if(!prs_uint32("CodePage", ps, depth, &object.CodePage)) + return False; + if(!prs_align_uint64(ps)) + return False; + if(!prs_uint64("PerfTime", ps, depth, &object.PerfTime)) + return False; + if(!prs_uint64("PerfFreq", ps, depth, &object.PerfFreq)) + return False; + + /* Now do the counters */ + /* If no instances, encode counter_data */ + /* If instances, encode instace plus counter data for each instance */ + if(_reg_perfcount_marshall_perf_counters(ps, object, depth) == False) + return False; + if(object.NumInstances == PERF_NO_INSTANCES) + { + if(_reg_perfcount_marshall_perf_counter_data(ps, object.counter_data, depth) == False) + return False; + } + else + { + if(_reg_perfcount_marshall_perf_instances(ps, object, depth) == False) + return False; + } + } + + return True; +} + +/********************************************************************* +*********************************************************************/ + +static bool _reg_perfcount_marshall_hkpd(prs_struct *ps, PERF_DATA_BLOCK block) +{ + int depth = 0; + if(_reg_perfcount_marshall_perf_data_block(ps, block, depth) == True) + { + if(_reg_perfcount_marshall_perf_objects(ps, block, depth) == True) + return True; + } + return False; +} + +/********************************************************************* +*********************************************************************/ + +WERROR reg_perfcount_get_hkpd(prs_struct *ps, uint32 max_buf_size, uint32 *outbuf_len, const char *object_ids) +{ + /* + * For a detailed description of the layout of this structure, + * see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/perfmon/base/performance_data_format.asp + * + * By 2006-11-23 this link did not work anymore, I found something + * promising under + * http://msdn2.microsoft.com/en-us/library/aa373105.aspx -- vl + */ + PERF_DATA_BLOCK block; + uint32 buffer_size, base_index; + + buffer_size = 0; + base_index = reg_perfcount_get_base_index(); + ZERO_STRUCT(block); + + buffer_size = reg_perfcount_get_perf_data_block(base_index, ps, &block, object_ids); + + if(buffer_size < max_buf_size) + { + *outbuf_len = buffer_size; + if(_reg_perfcount_marshall_hkpd(ps, block) == True) + return WERR_OK; + else + return WERR_NOMEM; + } + else + { + *outbuf_len = max_buf_size; + _reg_perfcount_marshall_perf_data_block(ps, block, 0); + return WERR_INSUFFICIENT_BUFFER; + } +} diff --git a/source3/registry/reg_util.c b/source3/registry/reg_util.c new file mode 100644 index 0000000000..714a39f307 --- /dev/null +++ b/source3/registry/reg_util.c @@ -0,0 +1,267 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer (utility functions) + * Copyright (C) Gerald Carter 2002-2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Implementation of registry frontend view functions. */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +/*********************************************************************** + 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; +} + +/*********************************************************************** + Utility function for splitting the base path of a registry path off + by setting base and new_path to the appropriate offsets withing the + path. + + WARNING!! Does modify the original string! + ***********************************************************************/ + +bool reg_split_key(char *path, char **base, char **key) +{ + char *p; + + *key = *base = NULL; + + if (!path) { + return false; + } + + *base = path; + + p = strrchr(path, '\\'); + + if (p) { + *p = '\0'; + *key = p+1; + } + + return true; +} + +/** + * The full path to the registry key is used as database key + * after the \'s are converted to /'s. + * Leading and trailing '/' and '\' characters are stripped. + * Key string is also normalized to UPPER case. + */ + +char *normalize_reg_path(TALLOC_CTX *ctx, const char *keyname ) +{ + char *p; + char *nkeyname; + + /* skip leading '/' and '\' chars */ + p = (char *)keyname; + while ((*p == '/') || (*p == '\\')) { + p++; + } + + nkeyname = talloc_string_sub(ctx, p, "\\", "/"); + if (nkeyname == NULL) { + return NULL; + } + + /* strip trailing '/' chars */ + p = strrchr(nkeyname, '/'); + while ((p != NULL) && (p[1] == '\0')) { + *p = '\0'; + p = strrchr(nkeyname, '/'); + } + + strupper_m(nkeyname); + + return nkeyname; +} + +/** + * normalize ther registry path in place. + */ +void normalize_dbkey(char *key) +{ + size_t len = strlen(key); + string_sub(key, "\\", "/", len+1); + strupper_m(key); +} + +/********************************************************************** + move to next non-delimter character +*********************************************************************/ + +char *reg_remaining_path(TALLOC_CTX *ctx, const char *key) +{ + char *new_path = NULL; + char *p = NULL; + + if (!key || !*key) { + return NULL; + } + + new_path = talloc_strdup(ctx, key); + if (!new_path) { + return NULL; + } + /* normalize_reg_path( new_path ); */ + if (!(p = strchr(new_path, '\\')) ) { + if (!(p = strchr( new_path, '/'))) { + p = new_path; + } else { + p++; + } + } else { + p++; + } + + return p; +} + +/********************************************************************** +*********************************************************************/ + +int regval_convert_multi_sz( uint16 *multi_string, size_t byte_len, char ***values ) +{ + char **sz; + int i; + int num_strings = 0; + fstring buffer; + uint16 *wp; + size_t multi_len = byte_len / 2; + + if ( !multi_string || !values ) + return 0; + + *values = NULL; + + /* just count the NULLs */ + + for ( i=0; (i<multi_len-1) && !(multi_string[i]==0x0 && multi_string[i+1]==0x0); i++ ) { + /* peek ahead */ + if ( multi_string[i+1] == 0x0 ) + num_strings++; + } + + if ( num_strings == 0 ) + return 0; + + if ( !(sz = TALLOC_ARRAY( NULL, char*, num_strings+1 )) ) { + DEBUG(0,("reg_convert_multi_sz: talloc() failed!\n")); + return -1; + } + + wp = multi_string; + + for ( i=0; i<num_strings; i++ ) { + rpcstr_pull( buffer, wp, sizeof(buffer), -1, STR_TERMINATE ); + sz[i] = talloc_strdup( sz, buffer ); + + /* skip to the next string NULL and then one more */ + while ( *wp ) + wp++; + wp++; + } + + /* tag the array off with an empty string */ + sz[i] = '\0'; + + *values = sz; + + return num_strings; +} + +/********************************************************************** + Returns number of bytes, not number of unicode characters +*********************************************************************/ + +size_t regval_build_multi_sz( char **values, uint16 **buffer ) +{ + int i; + size_t buf_size = 0; + uint16 *buf, *b; + UNISTR2 sz; + + if ( !values || !buffer ) + return 0; + + /* go ahead and alloc some space */ + + if ( !(buf = TALLOC_ARRAY( NULL, uint16, 2 )) ) { + DEBUG(0,("regval_build_multi_sz: talloc() failed!\n")); + return 0; + } + + for ( i=0; values[i]; i++ ) { + ZERO_STRUCT( sz ); + /* DEBUG(0,("regval_build_multi_sz: building [%s]\n",values[i])); */ + init_unistr2( &sz, values[i], UNI_STR_TERMINATE ); + + /* Alloc some more memory. Always add one one to account for the + double NULL termination */ + + b = TALLOC_REALLOC_ARRAY( NULL, buf, uint16, buf_size+sz.uni_str_len+1 ); + if ( !b ) { + DEBUG(0,("regval_build_multi_sz: talloc() reallocation error!\n")); + TALLOC_FREE( buffer ); + return 0; + } + buf = b; + + /* copy the unistring2 buffer and increment the size */ + /* dump_data(1,sz.buffer,sz.uni_str_len*2); */ + memcpy( buf+buf_size, sz.buffer, sz.uni_str_len*2 ); + buf_size += sz.uni_str_len; + + /* cleanup rather than leaving memory hanging around */ + TALLOC_FREE( sz.buffer ); + } + + buf[buf_size++] = 0x0; + + *buffer = buf; + + /* return number of bytes */ + return buf_size*2; +} diff --git a/source3/registry/reg_util_legacy.c b/source3/registry/reg_util_legacy.c new file mode 100644 index 0000000000..3e68025ae9 --- /dev/null +++ b/source3/registry/reg_util_legacy.c @@ -0,0 +1,47 @@ +/* + * Unix SMB/CIFS implementation. + * Virtual Windows Registry Layer + * Copyright (C) Gerald Carter 2002-2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Implementation of registry frontend view functions. */ + +#include "includes.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +/** + * legacy open key function that should be replaced by uses of + * reg_open_path + */ +WERROR regkey_open_internal( TALLOC_CTX *ctx, REGISTRY_KEY **regkey, + const char *path, + const struct nt_user_token *token, + uint32 access_desired ) +{ + struct registry_key *key; + WERROR err; + + err = reg_open_path(NULL, path, access_desired, token, &key); + if (!W_ERROR_IS_OK(err)) { + return err; + } + + *regkey = talloc_move(ctx, &key->key); + TALLOC_FREE(key); + return WERR_OK; +} diff --git a/source3/registry/regfio.c b/source3/registry/regfio.c new file mode 100644 index 0000000000..b6e822955c --- /dev/null +++ b/source3/registry/regfio.c @@ -0,0 +1,1915 @@ +/* + * Unix SMB/CIFS implementation. + * Windows NT registry I/O library + * Copyright (c) Gerald (Jerry) Carter 2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "regfio.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_REGISTRY + +/******************************************************************* + * + * TODO : Right now this code basically ignores classnames. + * + ******************************************************************/ + + +/******************************************************************* +*******************************************************************/ + +static int write_block( REGF_FILE *file, prs_struct *ps, uint32 offset ) +{ + int bytes_written, returned; + char *buffer = prs_data_p( ps ); + uint32 buffer_size = prs_data_size( ps ); + SMB_STRUCT_STAT sbuf; + + if ( file->fd == -1 ) + return -1; + + /* check for end of file */ + + if ( sys_fstat( file->fd, &sbuf ) ) { + DEBUG(0,("write_block: stat() failed! (%s)\n", strerror(errno))); + return -1; + } + + if ( lseek( file->fd, offset, SEEK_SET ) == -1 ) { + DEBUG(0,("write_block: lseek() failed! (%s)\n", strerror(errno) )); + return -1; + } + + bytes_written = returned = 0; + while ( bytes_written < buffer_size ) { + if ( (returned = write( file->fd, buffer+bytes_written, buffer_size-bytes_written )) == -1 ) { + DEBUG(0,("write_block: write() failed! (%s)\n", strerror(errno) )); + return False; + } + + bytes_written += returned; + } + + return bytes_written; +} + +/******************************************************************* +*******************************************************************/ + +static int read_block( REGF_FILE *file, prs_struct *ps, uint32 file_offset, uint32 block_size ) +{ + int bytes_read, returned; + char *buffer; + SMB_STRUCT_STAT sbuf; + + /* check for end of file */ + + if ( sys_fstat( file->fd, &sbuf ) ) { + DEBUG(0,("read_block: stat() failed! (%s)\n", strerror(errno))); + return -1; + } + + if ( (size_t)file_offset >= sbuf.st_size ) + return -1; + + /* if block_size == 0, we are parsing HBIN records and need + to read some of the header to get the block_size from there */ + + if ( block_size == 0 ) { + char hdr[0x20]; + + if ( lseek( file->fd, file_offset, SEEK_SET ) == -1 ) { + DEBUG(0,("read_block: lseek() failed! (%s)\n", strerror(errno) )); + return -1; + } + + returned = read( file->fd, hdr, 0x20 ); + if ( (returned == -1) || (returned < 0x20) ) { + DEBUG(0,("read_block: failed to read in HBIN header. Is the file corrupt?\n")); + return -1; + } + + /* make sure this is an hbin header */ + + if ( strncmp( hdr, "hbin", HBIN_HDR_SIZE ) != 0 ) { + DEBUG(0,("read_block: invalid block header!\n")); + return -1; + } + + block_size = IVAL( hdr, 0x08 ); + } + + DEBUG(10,("read_block: block_size == 0x%x\n", block_size )); + + /* set the offset, initialize the buffer, and read the block from disk */ + + if ( lseek( file->fd, file_offset, SEEK_SET ) == -1 ) { + DEBUG(0,("read_block: lseek() failed! (%s)\n", strerror(errno) )); + return -1; + } + + if (!prs_init( ps, block_size, file->mem_ctx, UNMARSHALL )) { + DEBUG(0,("read_block: prs_init() failed! (%s)\n", strerror(errno) )); + return -1; + } + buffer = prs_data_p( ps ); + bytes_read = returned = 0; + + while ( bytes_read < block_size ) { + if ( (returned = read( file->fd, buffer+bytes_read, block_size-bytes_read )) == -1 ) { + DEBUG(0,("read_block: read() failed (%s)\n", strerror(errno) )); + return False; + } + if ( (returned == 0) && (bytes_read < block_size) ) { + DEBUG(0,("read_block: not a vald registry file ?\n" )); + return False; + } + + bytes_read += returned; + } + + return bytes_read; +} + +/******************************************************************* +*******************************************************************/ + +static bool write_hbin_block( REGF_FILE *file, REGF_HBIN *hbin ) +{ + if ( !hbin->dirty ) + return True; + + /* write free space record if any is available */ + + if ( hbin->free_off != REGF_OFFSET_NONE ) { + uint32 header = 0xffffffff; + + if ( !prs_set_offset( &hbin->ps, hbin->free_off-sizeof(uint32) ) ) + return False; + if ( !prs_uint32( "free_size", &hbin->ps, 0, &hbin->free_size ) ) + return False; + if ( !prs_uint32( "free_header", &hbin->ps, 0, &header ) ) + return False; + } + + hbin->dirty = (write_block( file, &hbin->ps, hbin->file_off ) != -1); + + return hbin->dirty; +} + +/******************************************************************* +*******************************************************************/ + +static bool hbin_block_close( REGF_FILE *file, REGF_HBIN *hbin ) +{ + REGF_HBIN *p; + + /* remove the block from the open list and flush it to disk */ + + for ( p=file->block_list; p && p!=hbin; p=p->next ) + ;; + + if ( p == hbin ) { + DLIST_REMOVE( file->block_list, hbin ); + } + else + DEBUG(0,("hbin_block_close: block not in open list!\n")); + + if ( !write_hbin_block( file, hbin ) ) + return False; + + return True; +} + +/******************************************************************* +*******************************************************************/ + +static bool prs_regf_block( const char *desc, prs_struct *ps, int depth, REGF_FILE *file ) +{ + prs_debug(ps, depth, desc, "prs_regf_block"); + depth++; + + if ( !prs_uint8s( True, "header", ps, depth, (uint8*)file->header, sizeof( file->header )) ) + return False; + + /* yes, these values are always identical so store them only once */ + + if ( !prs_uint32( "unknown1", ps, depth, &file->unknown1 )) + return False; + if ( !prs_uint32( "unknown1 (again)", ps, depth, &file->unknown1 )) + return False; + + /* get the modtime */ + + if ( !prs_set_offset( ps, 0x0c ) ) + return False; + if ( !smb_io_time( "modtime", &file->mtime, ps, depth ) ) + return False; + + /* constants */ + + if ( !prs_uint32( "unknown2", ps, depth, &file->unknown2 )) + return False; + if ( !prs_uint32( "unknown3", ps, depth, &file->unknown3 )) + return False; + if ( !prs_uint32( "unknown4", ps, depth, &file->unknown4 )) + return False; + if ( !prs_uint32( "unknown5", ps, depth, &file->unknown5 )) + return False; + + /* get file offsets */ + + if ( !prs_set_offset( ps, 0x24 ) ) + return False; + if ( !prs_uint32( "data_offset", ps, depth, &file->data_offset )) + return False; + if ( !prs_uint32( "last_block", ps, depth, &file->last_block )) + return False; + + /* one more constant */ + + if ( !prs_uint32( "unknown6", ps, depth, &file->unknown6 )) + return False; + + /* get the checksum */ + + if ( !prs_set_offset( ps, 0x01fc ) ) + return False; + if ( !prs_uint32( "checksum", ps, depth, &file->checksum )) + return False; + + return True; +} + +/******************************************************************* +*******************************************************************/ + +static bool prs_hbin_block( const char *desc, prs_struct *ps, int depth, REGF_HBIN *hbin ) +{ + uint32 block_size2; + + prs_debug(ps, depth, desc, "prs_regf_block"); + depth++; + + if ( !prs_uint8s( True, "header", ps, depth, (uint8*)hbin->header, sizeof( hbin->header )) ) + return False; + + if ( !prs_uint32( "first_hbin_off", ps, depth, &hbin->first_hbin_off )) + return False; + + /* The dosreg.cpp comments say that the block size is at 0x1c. + According to a WINXP NTUSER.dat file, this is wrong. The block_size + is at 0x08 */ + + if ( !prs_uint32( "block_size", ps, depth, &hbin->block_size )) + return False; + + block_size2 = hbin->block_size; + prs_set_offset( ps, 0x1c ); + if ( !prs_uint32( "block_size2", ps, depth, &block_size2 )) + return False; + + if ( MARSHALLING(ps) ) + hbin->dirty = True; + + + return True; +} + +/******************************************************************* +*******************************************************************/ + +static bool prs_nk_rec( const char *desc, prs_struct *ps, int depth, REGF_NK_REC *nk ) +{ + uint16 class_length, name_length; + uint32 start; + uint32 data_size, start_off, end_off; + uint32 unknown_off = REGF_OFFSET_NONE; + + nk->hbin_off = prs_offset( ps ); + start = nk->hbin_off; + + prs_debug(ps, depth, desc, "prs_nk_rec"); + depth++; + + /* back up and get the data_size */ + + if ( !prs_set_offset( ps, prs_offset(ps)-sizeof(uint32)) ) + return False; + start_off = prs_offset( ps ); + if ( !prs_uint32( "rec_size", ps, depth, &nk->rec_size )) + return False; + + if ( !prs_uint8s( True, "header", ps, depth, (uint8*)nk->header, sizeof( nk->header )) ) + return False; + + if ( !prs_uint16( "key_type", ps, depth, &nk->key_type )) + return False; + if ( !smb_io_time( "mtime", &nk->mtime, ps, depth )) + return False; + + if ( !prs_set_offset( ps, start+0x0010 ) ) + return False; + if ( !prs_uint32( "parent_off", ps, depth, &nk->parent_off )) + return False; + if ( !prs_uint32( "num_subkeys", ps, depth, &nk->num_subkeys )) + return False; + + if ( !prs_set_offset( ps, start+0x001c ) ) + return False; + if ( !prs_uint32( "subkeys_off", ps, depth, &nk->subkeys_off )) + return False; + if ( !prs_uint32( "unknown_off", ps, depth, &unknown_off) ) + return False; + + if ( !prs_set_offset( ps, start+0x0024 ) ) + return False; + if ( !prs_uint32( "num_values", ps, depth, &nk->num_values )) + return False; + if ( !prs_uint32( "values_off", ps, depth, &nk->values_off )) + return False; + if ( !prs_uint32( "sk_off", ps, depth, &nk->sk_off )) + return False; + if ( !prs_uint32( "classname_off", ps, depth, &nk->classname_off )) + return False; + + if ( !prs_uint32( "max_bytes_subkeyname", ps, depth, &nk->max_bytes_subkeyname)) + return False; + if ( !prs_uint32( "max_bytes_subkeyclassname", ps, depth, &nk->max_bytes_subkeyclassname)) + return False; + if ( !prs_uint32( "max_bytes_valuename", ps, depth, &nk->max_bytes_valuename)) + return False; + if ( !prs_uint32( "max_bytes_value", ps, depth, &nk->max_bytes_value)) + return False; + if ( !prs_uint32( "unknown index", ps, depth, &nk->unk_index)) + return False; + + name_length = nk->keyname ? strlen(nk->keyname) : 0 ; + class_length = nk->classname ? strlen(nk->classname) : 0 ; + if ( !prs_uint16( "name_length", ps, depth, &name_length )) + return False; + if ( !prs_uint16( "class_length", ps, depth, &class_length )) + return False; + + if ( class_length ) { + ;; + } + + if ( name_length ) { + if ( UNMARSHALLING(ps) ) { + if ( !(nk->keyname = PRS_ALLOC_MEM( ps, char, name_length+1 )) ) + return False; + } + + if ( !prs_uint8s( True, "name", ps, depth, (uint8*)nk->keyname, name_length) ) + return False; + + if ( UNMARSHALLING(ps) ) + nk->keyname[name_length] = '\0'; + } + + end_off = prs_offset( ps ); + + /* data_size must be divisible by 8 and large enough to hold the original record */ + + data_size = ((start_off - end_off) & 0xfffffff8 ); + if ( data_size > nk->rec_size ) + DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, nk->rec_size)); + + if ( MARSHALLING(ps) ) + nk->hbin->dirty = True; + + return True; +} + +/******************************************************************* +*******************************************************************/ + +static uint32 regf_block_checksum( prs_struct *ps ) +{ + char *buffer = prs_data_p( ps ); + uint32 checksum, x; + int i; + + /* XOR of all bytes 0x0000 - 0x01FB */ + + checksum = x = 0; + + for ( i=0; i<0x01FB; i+=4 ) { + x = IVAL(buffer, i ); + checksum ^= x; + } + + return checksum; +} + +/******************************************************************* +*******************************************************************/ + +static bool read_regf_block( REGF_FILE *file ) +{ + prs_struct ps; + uint32 checksum; + + /* grab the first block from the file */ + + if ( read_block( file, &ps, 0, REGF_BLOCKSIZE ) == -1 ) + return False; + + /* parse the block and verify the checksum */ + + if ( !prs_regf_block( "regf_header", &ps, 0, file ) ) + return False; + + checksum = regf_block_checksum( &ps ); + + prs_mem_free( &ps ); + + if ( file->checksum != checksum ) { + DEBUG(0,("read_regf_block: invalid checksum\n" )); + return False; + } + + return True; +} + +/******************************************************************* +*******************************************************************/ + +static REGF_HBIN* read_hbin_block( REGF_FILE *file, off_t offset ) +{ + REGF_HBIN *hbin; + uint32 record_size, curr_off, block_size, header; + + if ( !(hbin = TALLOC_ZERO_P(file->mem_ctx, REGF_HBIN)) ) + return NULL; + hbin->file_off = offset; + hbin->free_off = -1; + + if ( read_block( file, &hbin->ps, offset, 0 ) == -1 ) + return NULL; + + if ( !prs_hbin_block( "hbin", &hbin->ps, 0, hbin ) ) + return NULL; + + /* this should be the same thing as hbin->block_size but just in case */ + + block_size = prs_data_size( &hbin->ps ); + + /* Find the available free space offset. Always at the end, + so walk the record list and stop when you get to the end. + The end is defined by a record header of 0xffffffff. The + previous 4 bytes contains the amount of free space remaining + in the hbin block. */ + + /* remember that the record_size is in the 4 bytes preceeding the record itself */ + + if ( !prs_set_offset( &hbin->ps, file->data_offset+HBIN_HDR_SIZE-sizeof(uint32) ) ) + return False; + + record_size = 0; + header = 0; + curr_off = prs_offset( &hbin->ps ); + while ( header != 0xffffffff ) { + /* not done yet so reset the current offset to the + next record_size field */ + + curr_off = curr_off+record_size; + + /* for some reason the record_size of the last record in + an hbin block can extend past the end of the block + even though the record fits within the remaining + space....aaarrrgggghhhhhh */ + + if ( curr_off >= block_size ) { + record_size = -1; + curr_off = -1; + break; + } + + if ( !prs_set_offset( &hbin->ps, curr_off) ) + return False; + + if ( !prs_uint32( "rec_size", &hbin->ps, 0, &record_size ) ) + return False; + if ( !prs_uint32( "header", &hbin->ps, 0, &header ) ) + return False; + + SMB_ASSERT( record_size != 0 ); + + if ( record_size & 0x80000000 ) { + /* absolute_value(record_size) */ + record_size = (record_size ^ 0xffffffff) + 1; + } + } + + /* save the free space offset */ + + if ( header == 0xffffffff ) { + + /* account for the fact that the curr_off is 4 bytes behind the actual + record header */ + + hbin->free_off = curr_off + sizeof(uint32); + hbin->free_size = record_size; + } + + DEBUG(10,("read_hbin_block: free space offset == 0x%x\n", hbin->free_off)); + + if ( !prs_set_offset( &hbin->ps, file->data_offset+HBIN_HDR_SIZE ) ) + return False; + + return hbin; +} + +/******************************************************************* + Input a random offset and receive the corresponding HBIN + block for it +*******************************************************************/ + +static bool hbin_contains_offset( REGF_HBIN *hbin, uint32 offset ) +{ + if ( !hbin ) + return False; + + if ( (offset > hbin->first_hbin_off) && (offset < (hbin->first_hbin_off+hbin->block_size)) ) + return True; + + return False; +} + +/******************************************************************* + Input a random offset and receive the corresponding HBIN + block for it +*******************************************************************/ + +static REGF_HBIN* lookup_hbin_block( REGF_FILE *file, uint32 offset ) +{ + REGF_HBIN *hbin = NULL; + uint32 block_off; + + /* start with the open list */ + + for ( hbin=file->block_list; hbin; hbin=hbin->next ) { + DEBUG(10,("lookup_hbin_block: address = 0x%x [0x%lx]\n", hbin->file_off, (unsigned long)hbin )); + if ( hbin_contains_offset( hbin, offset ) ) + return hbin; + } + + if ( !hbin ) { + /* start at the beginning */ + + block_off = REGF_BLOCKSIZE; + do { + /* cleanup before the next round */ + if ( hbin ) + prs_mem_free( &hbin->ps ); + + hbin = read_hbin_block( file, block_off ); + + if ( hbin ) + block_off = hbin->file_off + hbin->block_size; + + } while ( hbin && !hbin_contains_offset( hbin, offset ) ); + } + + if ( hbin ) + DLIST_ADD( file->block_list, hbin ); + + return hbin; +} + +/******************************************************************* +*******************************************************************/ + +static bool prs_hash_rec( const char *desc, prs_struct *ps, int depth, REGF_HASH_REC *hash ) +{ + prs_debug(ps, depth, desc, "prs_hash_rec"); + depth++; + + if ( !prs_uint32( "nk_off", ps, depth, &hash->nk_off )) + return False; + if ( !prs_uint8s( True, "keycheck", ps, depth, hash->keycheck, sizeof( hash->keycheck )) ) + return False; + + return True; +} + +/******************************************************************* +*******************************************************************/ + +static bool hbin_prs_lf_records( const char *desc, REGF_HBIN *hbin, int depth, REGF_NK_REC *nk ) +{ + int i; + REGF_LF_REC *lf = &nk->subkeys; + uint32 data_size, start_off, end_off; + + prs_debug(&hbin->ps, depth, desc, "prs_lf_records"); + depth++; + + /* check if we have anything to do first */ + + if ( nk->num_subkeys == 0 ) + return True; + + /* move to the LF record */ + + if ( !prs_set_offset( &hbin->ps, nk->subkeys_off + HBIN_HDR_SIZE - hbin->first_hbin_off ) ) + return False; + + /* backup and get the data_size */ + + if ( !prs_set_offset( &hbin->ps, prs_offset(&hbin->ps)-sizeof(uint32)) ) + return False; + start_off = prs_offset( &hbin->ps ); + if ( !prs_uint32( "rec_size", &hbin->ps, depth, &lf->rec_size )) + return False; + + if ( !prs_uint8s( True, "header", &hbin->ps, depth, (uint8*)lf->header, sizeof( lf->header )) ) + return False; + + if ( !prs_uint16( "num_keys", &hbin->ps, depth, &lf->num_keys)) + return False; + + if ( UNMARSHALLING(&hbin->ps) ) { + if (lf->num_keys) { + if ( !(lf->hashes = PRS_ALLOC_MEM( &hbin->ps, REGF_HASH_REC, lf->num_keys )) ) + return False; + } else { + lf->hashes = NULL; + } + } + + for ( i=0; i<lf->num_keys; i++ ) { + if ( !prs_hash_rec( "hash_rec", &hbin->ps, depth, &lf->hashes[i] ) ) + return False; + } + + end_off = prs_offset( &hbin->ps ); + + /* data_size must be divisible by 8 and large enough to hold the original record */ + + data_size = ((start_off - end_off) & 0xfffffff8 ); + if ( data_size > lf->rec_size ) + DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, lf->rec_size)); + + if ( MARSHALLING(&hbin->ps) ) + hbin->dirty = True; + + return True; +} + +/******************************************************************* +*******************************************************************/ + +static bool hbin_prs_sk_rec( const char *desc, REGF_HBIN *hbin, int depth, REGF_SK_REC *sk ) +{ + prs_struct *ps = &hbin->ps; + uint16 tag = 0xFFFF; + uint32 data_size, start_off, end_off; + + + prs_debug(ps, depth, desc, "hbin_prs_sk_rec"); + depth++; + + if ( !prs_set_offset( &hbin->ps, sk->sk_off + HBIN_HDR_SIZE - hbin->first_hbin_off ) ) + return False; + + /* backup and get the data_size */ + + if ( !prs_set_offset( &hbin->ps, prs_offset(&hbin->ps)-sizeof(uint32)) ) + return False; + start_off = prs_offset( &hbin->ps ); + if ( !prs_uint32( "rec_size", &hbin->ps, depth, &sk->rec_size )) + return False; + + if ( !prs_uint8s( True, "header", ps, depth, (uint8*)sk->header, sizeof( sk->header )) ) + return False; + if ( !prs_uint16( "tag", ps, depth, &tag)) + return False; + + if ( !prs_uint32( "prev_sk_off", ps, depth, &sk->prev_sk_off)) + return False; + if ( !prs_uint32( "next_sk_off", ps, depth, &sk->next_sk_off)) + return False; + if ( !prs_uint32( "ref_count", ps, depth, &sk->ref_count)) + return False; + if ( !prs_uint32( "size", ps, depth, &sk->size)) + return False; + + if ( !sec_io_desc( "sec_desc", &sk->sec_desc, ps, depth )) + return False; + + end_off = prs_offset( &hbin->ps ); + + /* data_size must be divisible by 8 and large enough to hold the original record */ + + data_size = ((start_off - end_off) & 0xfffffff8 ); + if ( data_size > sk->rec_size ) + DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, sk->rec_size)); + + if ( MARSHALLING(&hbin->ps) ) + hbin->dirty = True; + + return True; +} + +/******************************************************************* +*******************************************************************/ + +static bool hbin_prs_vk_rec( const char *desc, REGF_HBIN *hbin, int depth, REGF_VK_REC *vk, REGF_FILE *file ) +{ + uint32 offset; + uint16 name_length; + prs_struct *ps = &hbin->ps; + uint32 data_size, start_off, end_off; + + prs_debug(ps, depth, desc, "prs_vk_rec"); + depth++; + + /* backup and get the data_size */ + + if ( !prs_set_offset( &hbin->ps, prs_offset(&hbin->ps)-sizeof(uint32)) ) + return False; + start_off = prs_offset( &hbin->ps ); + if ( !prs_uint32( "rec_size", &hbin->ps, depth, &vk->rec_size )) + return False; + + if ( !prs_uint8s( True, "header", ps, depth, (uint8*)vk->header, sizeof( vk->header )) ) + return False; + + if ( MARSHALLING(&hbin->ps) ) + name_length = strlen(vk->valuename); + + if ( !prs_uint16( "name_length", ps, depth, &name_length )) + return False; + if ( !prs_uint32( "data_size", ps, depth, &vk->data_size )) + return False; + if ( !prs_uint32( "data_off", ps, depth, &vk->data_off )) + return False; + if ( !prs_uint32( "type", ps, depth, &vk->type)) + return False; + if ( !prs_uint16( "flag", ps, depth, &vk->flag)) + return False; + + offset = prs_offset( ps ); + offset += 2; /* skip 2 bytes */ + prs_set_offset( ps, offset ); + + /* get the name */ + + if ( vk->flag&VK_FLAG_NAME_PRESENT ) { + + if ( UNMARSHALLING(&hbin->ps) ) { + if ( !(vk->valuename = PRS_ALLOC_MEM( ps, char, name_length+1 ))) + return False; + } + if ( !prs_uint8s( True, "name", ps, depth, (uint8*)vk->valuename, name_length ) ) + return False; + } + + end_off = prs_offset( &hbin->ps ); + + /* get the data if necessary */ + + if ( vk->data_size != 0 ) { + bool charmode = False; + + if ( (vk->type == REG_SZ) || (vk->type == REG_MULTI_SZ) ) + charmode = True; + + /* the data is stored in the offset if the size <= 4 */ + + if ( !(vk->data_size & VK_DATA_IN_OFFSET) ) { + REGF_HBIN *hblock = hbin; + uint32 data_rec_size; + + if ( UNMARSHALLING(&hbin->ps) ) { + if ( !(vk->data = PRS_ALLOC_MEM( ps, uint8, vk->data_size) ) ) + return False; + } + + /* this data can be in another hbin */ + if ( !hbin_contains_offset( hbin, vk->data_off ) ) { + if ( !(hblock = lookup_hbin_block( file, vk->data_off )) ) + return False; + } + if ( !(prs_set_offset( &hblock->ps, (vk->data_off+HBIN_HDR_SIZE-hblock->first_hbin_off)-sizeof(uint32) )) ) + return False; + + if ( MARSHALLING(&hblock->ps) ) { + data_rec_size = ( (vk->data_size+sizeof(uint32)) & 0xfffffff8 ) + 8; + data_rec_size = ( data_rec_size - 1 ) ^ 0xFFFFFFFF; + } + if ( !prs_uint32( "data_rec_size", &hblock->ps, depth, &data_rec_size )) + return False; + if ( !prs_uint8s( charmode, "data", &hblock->ps, depth, vk->data, vk->data_size) ) + return False; + + if ( MARSHALLING(&hblock->ps) ) + hblock->dirty = True; + } + else { + if ( !(vk->data = PRS_ALLOC_MEM( ps, uint8, 4 ) ) ) + return False; + SIVAL( vk->data, 0, vk->data_off ); + } + + } + + /* data_size must be divisible by 8 and large enough to hold the original record */ + + data_size = ((start_off - end_off ) & 0xfffffff8 ); + if ( data_size != vk->rec_size ) + DEBUG(10,("prs_vk_rec: data_size check failed (0x%x < 0x%x)\n", data_size, vk->rec_size)); + + if ( MARSHALLING(&hbin->ps) ) + hbin->dirty = True; + + return True; +} + +/******************************************************************* + read a VK record which is contained in the HBIN block stored + in the prs_struct *ps. +*******************************************************************/ + +static bool hbin_prs_vk_records( const char *desc, REGF_HBIN *hbin, int depth, REGF_NK_REC *nk, REGF_FILE *file ) +{ + int i; + uint32 record_size; + + prs_debug(&hbin->ps, depth, desc, "prs_vk_records"); + depth++; + + /* check if we have anything to do first */ + + if ( nk->num_values == 0 ) + return True; + + if ( UNMARSHALLING(&hbin->ps) ) { + if ( !(nk->values = PRS_ALLOC_MEM( &hbin->ps, REGF_VK_REC, nk->num_values ) ) ) + return False; + } + + /* convert the offset to something relative to this HBIN block */ + + if ( !prs_set_offset( &hbin->ps, nk->values_off+HBIN_HDR_SIZE-hbin->first_hbin_off-sizeof(uint32)) ) + return False; + + if ( MARSHALLING( &hbin->ps) ) { + record_size = ( ( nk->num_values * sizeof(uint32) ) & 0xfffffff8 ) + 8; + record_size = (record_size - 1) ^ 0xFFFFFFFF; + } + + if ( !prs_uint32( "record_size", &hbin->ps, depth, &record_size ) ) + return False; + + for ( i=0; i<nk->num_values; i++ ) { + if ( !prs_uint32( "vk_off", &hbin->ps, depth, &nk->values[i].rec_off ) ) + return False; + } + + for ( i=0; i<nk->num_values; i++ ) { + REGF_HBIN *sub_hbin = hbin; + uint32 new_offset; + + if ( !hbin_contains_offset( hbin, nk->values[i].rec_off ) ) { + sub_hbin = lookup_hbin_block( file, nk->values[i].rec_off ); + if ( !sub_hbin ) { + DEBUG(0,("hbin_prs_vk_records: Failed to find HBIN block containing offset [0x%x]\n", + nk->values[i].hbin_off)); + return False; + } + } + + new_offset = nk->values[i].rec_off + HBIN_HDR_SIZE - sub_hbin->first_hbin_off; + if ( !prs_set_offset( &sub_hbin->ps, new_offset ) ) + return False; + if ( !hbin_prs_vk_rec( "vk_rec", sub_hbin, depth, &nk->values[i], file ) ) + return False; + } + + if ( MARSHALLING(&hbin->ps) ) + hbin->dirty = True; + + + return True; +} + + +/******************************************************************* +*******************************************************************/ + +static REGF_SK_REC* find_sk_record_by_offset( REGF_FILE *file, uint32 offset ) +{ + REGF_SK_REC *p_sk; + + for ( p_sk=file->sec_desc_list; p_sk; p_sk=p_sk->next ) { + if ( p_sk->sk_off == offset ) + return p_sk; + } + + return NULL; +} + +/******************************************************************* +*******************************************************************/ + +static REGF_SK_REC* find_sk_record_by_sec_desc( REGF_FILE *file, SEC_DESC *sd ) +{ + REGF_SK_REC *p; + + for ( p=file->sec_desc_list; p; p=p->next ) { + if ( sec_desc_equal( p->sec_desc, sd ) ) + return p; + } + + /* failure */ + + return NULL; +} + +/******************************************************************* +*******************************************************************/ + +static bool hbin_prs_key( REGF_FILE *file, REGF_HBIN *hbin, REGF_NK_REC *nk ) +{ + int depth = 0; + REGF_HBIN *sub_hbin; + + prs_debug(&hbin->ps, depth, "", "fetch_key"); + depth++; + + /* get the initial nk record */ + + if ( !prs_nk_rec( "nk_rec", &hbin->ps, depth, nk )) + return False; + + /* fill in values */ + + if ( nk->num_values && (nk->values_off!=REGF_OFFSET_NONE) ) { + sub_hbin = hbin; + if ( !hbin_contains_offset( hbin, nk->values_off ) ) { + sub_hbin = lookup_hbin_block( file, nk->values_off ); + if ( !sub_hbin ) { + DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing value_list_offset [0x%x]\n", + nk->values_off)); + return False; + } + } + + if ( !hbin_prs_vk_records( "vk_rec", sub_hbin, depth, nk, file )) + return False; + } + + /* now get subkeys */ + + if ( nk->num_subkeys && (nk->subkeys_off!=REGF_OFFSET_NONE) ) { + sub_hbin = hbin; + if ( !hbin_contains_offset( hbin, nk->subkeys_off ) ) { + sub_hbin = lookup_hbin_block( file, nk->subkeys_off ); + if ( !sub_hbin ) { + DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing subkey_offset [0x%x]\n", + nk->subkeys_off)); + return False; + } + } + + if ( !hbin_prs_lf_records( "lf_rec", sub_hbin, depth, nk )) + return False; + } + + /* get the to the security descriptor. First look if we have already parsed it */ + + if ( (nk->sk_off!=REGF_OFFSET_NONE) && !( nk->sec_desc = find_sk_record_by_offset( file, nk->sk_off )) ) { + + sub_hbin = hbin; + if ( !hbin_contains_offset( hbin, nk->sk_off ) ) { + sub_hbin = lookup_hbin_block( file, nk->sk_off ); + if ( !sub_hbin ) { + DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing sk_offset [0x%x]\n", + nk->subkeys_off)); + return False; + } + } + + if ( !(nk->sec_desc = TALLOC_ZERO_P( file->mem_ctx, REGF_SK_REC )) ) + return False; + nk->sec_desc->sk_off = nk->sk_off; + if ( !hbin_prs_sk_rec( "sk_rec", sub_hbin, depth, nk->sec_desc )) + return False; + + /* add to the list of security descriptors (ref_count has been read from the files) */ + + nk->sec_desc->sk_off = nk->sk_off; + DLIST_ADD( file->sec_desc_list, nk->sec_desc ); + } + + return True; +} + +/******************************************************************* +*******************************************************************/ + +static bool next_record( REGF_HBIN *hbin, const char *hdr, bool *eob ) +{ + uint8 header[REC_HDR_SIZE]; + uint32 record_size; + uint32 curr_off, block_size; + bool found = False; + prs_struct *ps = &hbin->ps; + + curr_off = prs_offset( ps ); + if ( curr_off == 0 ) + prs_set_offset( ps, HBIN_HEADER_REC_SIZE ); + + /* assume that the current offset is at the record header + and we need to backup to read the record size */ + + curr_off -= sizeof(uint32); + + block_size = prs_data_size( ps ); + record_size = 0; + memset( header, 0x0, sizeof(uint8)*REC_HDR_SIZE ); + while ( !found ) { + + curr_off = curr_off+record_size; + if ( curr_off >= block_size ) + break; + + if ( !prs_set_offset( &hbin->ps, curr_off) ) + return False; + + if ( !prs_uint32( "record_size", ps, 0, &record_size ) ) + return False; + if ( !prs_uint8s( True, "header", ps, 0, header, REC_HDR_SIZE ) ) + return False; + + if ( record_size & 0x80000000 ) { + /* absolute_value(record_size) */ + record_size = (record_size ^ 0xffffffff) + 1; + } + + if ( memcmp( header, hdr, REC_HDR_SIZE ) == 0 ) { + found = True; + curr_off += sizeof(uint32); + } + } + + /* mark prs_struct as done ( at end ) if no more SK records */ + /* mark end-of-block as True */ + + if ( !found ) { + prs_set_offset( &hbin->ps, prs_data_size(&hbin->ps) ); + *eob = True; + return False; + } + + if ( !prs_set_offset( ps, curr_off ) ) + return False; + + return True; +} + +/******************************************************************* +*******************************************************************/ + +static bool next_nk_record( REGF_FILE *file, REGF_HBIN *hbin, REGF_NK_REC *nk, bool *eob ) +{ + if ( next_record( hbin, "nk", eob ) && hbin_prs_key( file, hbin, nk ) ) + return True; + + return False; +} + +/******************************************************************* + Intialize the newly created REGF_BLOCK in *file and write the + block header to disk +*******************************************************************/ + +static bool init_regf_block( REGF_FILE *file ) +{ + prs_struct ps; + bool result = True; + + if ( !prs_init( &ps, REGF_BLOCKSIZE, file->mem_ctx, MARSHALL ) ) + return False; + + memcpy( file->header, "regf", REGF_HDR_SIZE ); + file->data_offset = 0x20; + file->last_block = 0x1000; + + /* set mod time */ + + unix_to_nt_time( &file->mtime, time(NULL) ); + + /* hard coded values...no diea what these are ... maybe in time */ + + file->unknown1 = 0x2; + file->unknown2 = 0x1; + file->unknown3 = 0x3; + file->unknown4 = 0x0; + file->unknown5 = 0x1; + file->unknown6 = 0x1; + + /* write header to the buffer */ + + if ( !prs_regf_block( "regf_header", &ps, 0, file ) ) { + result = False; + goto out; + } + + /* calculate the checksum, re-marshall data (to include the checksum) + and write to disk */ + + file->checksum = regf_block_checksum( &ps ); + prs_set_offset( &ps, 0 ); + if ( !prs_regf_block( "regf_header", &ps, 0, file ) ) { + result = False; + goto out; + } + + if ( write_block( file, &ps, 0 ) == -1 ) { + DEBUG(0,("init_regf_block: Failed to initialize registry header block!\n")); + result = False; + goto out; + } + +out: + prs_mem_free( &ps ); + + return result; +} +/******************************************************************* + Open the registry file and then read in the REGF block to get the + first hbin offset. +*******************************************************************/ + + REGF_FILE* regfio_open( const char *filename, int flags, int mode ) +{ + REGF_FILE *rb; + + if ( !(rb = SMB_MALLOC_P(REGF_FILE)) ) { + DEBUG(0,("ERROR allocating memory\n")); + return NULL; + } + ZERO_STRUCTP( rb ); + rb->fd = -1; + + if ( !(rb->mem_ctx = talloc_init( "read_regf_block" )) ) { + regfio_close( rb ); + return NULL; + } + + rb->open_flags = flags; + + /* open and existing file */ + + if ( (rb->fd = open(filename, flags, mode)) == -1 ) { + DEBUG(0,("regfio_open: failure to open %s (%s)\n", filename, strerror(errno))); + regfio_close( rb ); + return NULL; + } + + /* check if we are creating a new file or overwriting an existing one */ + + if ( flags & (O_CREAT|O_TRUNC) ) { + if ( !init_regf_block( rb ) ) { + DEBUG(0,("regfio_open: Failed to read initial REGF block\n")); + regfio_close( rb ); + return NULL; + } + + /* success */ + return rb; + } + + /* read in an existing file */ + + if ( !read_regf_block( rb ) ) { + DEBUG(0,("regfio_open: Failed to read initial REGF block\n")); + regfio_close( rb ); + return NULL; + } + + /* success */ + + return rb; +} + +/******************************************************************* +*******************************************************************/ + +static void regfio_mem_free( REGF_FILE *file ) +{ + /* free any talloc()'d memory */ + + if ( file && file->mem_ctx ) + talloc_destroy( file->mem_ctx ); +} + +/******************************************************************* +*******************************************************************/ + + int regfio_close( REGF_FILE *file ) +{ + int fd; + + /* cleanup for a file opened for write */ + + if ((file->fd != -1) && (file->open_flags & (O_WRONLY|O_RDWR))) { + prs_struct ps; + REGF_SK_REC *sk; + + /* write of sd list */ + + for ( sk=file->sec_desc_list; sk; sk=sk->next ) { + hbin_prs_sk_rec( "sk_rec", sk->hbin, 0, sk ); + } + + /* flush any dirty blocks */ + + while ( file->block_list ) { + hbin_block_close( file, file->block_list ); + } + + ZERO_STRUCT( ps ); + + unix_to_nt_time( &file->mtime, time(NULL) ); + + if ( read_block( file, &ps, 0, REGF_BLOCKSIZE ) != -1 ) { + /* now use for writing */ + prs_switch_type( &ps, MARSHALL ); + + /* stream the block once, generate the checksum, + and stream it again */ + prs_set_offset( &ps, 0 ); + prs_regf_block( "regf_blocK", &ps, 0, file ); + file->checksum = regf_block_checksum( &ps ); + prs_set_offset( &ps, 0 ); + prs_regf_block( "regf_blocK", &ps, 0, file ); + + /* now we are ready to write it to disk */ + if ( write_block( file, &ps, 0 ) == -1 ) + DEBUG(0,("regfio_close: failed to update the regf header block!\n")); + } + + prs_mem_free( &ps ); + } + + regfio_mem_free( file ); + + /* nothing tdo do if there is no open file */ + + if (file->fd == -1) + return 0; + + fd = file->fd; + file->fd = -1; + SAFE_FREE( file ); + + return close( fd ); +} + +/******************************************************************* +*******************************************************************/ + +static void regfio_flush( REGF_FILE *file ) +{ + REGF_HBIN *hbin; + + for ( hbin=file->block_list; hbin; hbin=hbin->next ) { + write_hbin_block( file, hbin ); + } +} + +/******************************************************************* + There should be only *one* root key in the registry file based + on my experience. --jerry +*******************************************************************/ + +REGF_NK_REC* regfio_rootkey( REGF_FILE *file ) +{ + REGF_NK_REC *nk; + REGF_HBIN *hbin; + uint32 offset = REGF_BLOCKSIZE; + bool found = False; + bool eob; + + if ( !file ) + return NULL; + + if ( !(nk = TALLOC_ZERO_P( file->mem_ctx, REGF_NK_REC )) ) { + DEBUG(0,("regfio_rootkey: talloc() failed!\n")); + return NULL; + } + + /* scan through the file on HBIN block at a time looking + for an NK record with a type == 0x002c. + Normally this is the first nk record in the first hbin + block (but I'm not assuming that for now) */ + + while ( (hbin = read_hbin_block( file, offset )) ) { + eob = False; + + while ( !eob) { + if ( next_nk_record( file, hbin, nk, &eob ) ) { + if ( nk->key_type == NK_TYPE_ROOTKEY ) { + found = True; + break; + } + } + prs_mem_free( &hbin->ps ); + } + + if ( found ) + break; + + offset += hbin->block_size; + } + + if ( !found ) { + DEBUG(0,("regfio_rootkey: corrupt registry file ? No root key record located\n")); + return NULL; + } + + DLIST_ADD( file->block_list, hbin ); + + return nk; +} + +/******************************************************************* + This acts as an interator over the subkeys defined for a given + NK record. Remember that offsets are from the *first* HBIN block. +*******************************************************************/ + + REGF_NK_REC* regfio_fetch_subkey( REGF_FILE *file, REGF_NK_REC *nk ) +{ + REGF_NK_REC *subkey; + REGF_HBIN *hbin; + uint32 nk_offset; + + /* see if there is anything left to report */ + + if ( !nk || (nk->subkeys_off==REGF_OFFSET_NONE) || (nk->subkey_index >= nk->num_subkeys) ) + return NULL; + + /* find the HBIN block which should contain the nk record */ + + if ( !(hbin = lookup_hbin_block( file, nk->subkeys.hashes[nk->subkey_index].nk_off )) ) { + DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing offset [0x%x]\n", + nk->subkeys.hashes[nk->subkey_index].nk_off)); + return NULL; + } + + nk_offset = nk->subkeys.hashes[nk->subkey_index].nk_off; + if ( !prs_set_offset( &hbin->ps, (HBIN_HDR_SIZE + nk_offset - hbin->first_hbin_off) ) ) + return NULL; + + nk->subkey_index++; + if ( !(subkey = TALLOC_ZERO_P( file->mem_ctx, REGF_NK_REC )) ) + return NULL; + + if ( !hbin_prs_key( file, hbin, subkey ) ) + return NULL; + + return subkey; +} + + +/******************************************************************* +*******************************************************************/ + +static REGF_HBIN* regf_hbin_allocate( REGF_FILE *file, uint32 block_size ) +{ + REGF_HBIN *hbin; + SMB_STRUCT_STAT sbuf; + + if ( !(hbin = TALLOC_ZERO_P( file->mem_ctx, REGF_HBIN )) ) + return NULL; + + memcpy( hbin->header, "hbin", sizeof(HBIN_HDR_SIZE) ); + + + if ( sys_fstat( file->fd, &sbuf ) ) { + DEBUG(0,("regf_hbin_allocate: stat() failed! (%s)\n", strerror(errno))); + return NULL; + } + + hbin->file_off = sbuf.st_size; + + hbin->free_off = HBIN_HEADER_REC_SIZE; + hbin->free_size = block_size - hbin->free_off + sizeof(uint32);; + + hbin->block_size = block_size; + hbin->first_hbin_off = hbin->file_off - REGF_BLOCKSIZE; + + if ( !prs_init( &hbin->ps, block_size, file->mem_ctx, MARSHALL ) ) + return NULL; + + if ( !prs_hbin_block( "new_hbin", &hbin->ps, 0, hbin ) ) + return NULL; + + if ( !write_hbin_block( file, hbin ) ) + return NULL; + + file->last_block = hbin->file_off; + + return hbin; +} + +/******************************************************************* +*******************************************************************/ + +static void update_free_space( REGF_HBIN *hbin, uint32 size_used ) +{ + hbin->free_off += size_used; + hbin->free_size -= size_used; + + if ( hbin->free_off >= hbin->block_size ) { + hbin->free_off = REGF_OFFSET_NONE; + } + + return; +} + +/******************************************************************* +*******************************************************************/ + +static REGF_HBIN* find_free_space( REGF_FILE *file, uint32 size ) +{ + REGF_HBIN *hbin, *p_hbin; + uint32 block_off; + bool cached; + + /* check open block list */ + + for ( hbin=file->block_list; hbin!=NULL; hbin=hbin->next ) { + /* only check blocks that actually have available space */ + + if ( hbin->free_off == REGF_OFFSET_NONE ) + continue; + + /* check for a large enough available chunk */ + + if ( (hbin->block_size - hbin->free_off) >= size ) { + DLIST_PROMOTE( file->block_list, hbin ); + goto done; + } + } + + /* parse the file until we find a block with + enough free space; save the last non-filled hbin */ + + block_off = REGF_BLOCKSIZE; + do { + /* cleanup before the next round */ + cached = False; + if ( hbin ) + prs_mem_free( &hbin->ps ); + + hbin = read_hbin_block( file, block_off ); + + if ( hbin ) { + + /* make sure that we don't already have this block in memory */ + + for ( p_hbin=file->block_list; p_hbin!=NULL; p_hbin=p_hbin->next ) { + if ( p_hbin->file_off == hbin->file_off ) { + cached = True; + break; + } + } + + block_off = hbin->file_off + hbin->block_size; + + if ( cached ) { + prs_mem_free( &hbin->ps ); + hbin = NULL; + continue; + } + } + /* if (cached block or (new block and not enough free space)) then continue looping */ + } while ( cached || (hbin && (hbin->free_size < size)) ); + + /* no free space; allocate a new one */ + + if ( !hbin ) { + uint32 alloc_size; + + /* allocate in multiples of REGF_ALLOC_BLOCK; make sure (size + hbin_header) fits */ + + alloc_size = (((size+HBIN_HEADER_REC_SIZE) / REGF_ALLOC_BLOCK ) + 1 ) * REGF_ALLOC_BLOCK; + + if ( !(hbin = regf_hbin_allocate( file, alloc_size )) ) { + DEBUG(0,("find_free_space: regf_hbin_allocate() failed!\n")); + return NULL; + } + DLIST_ADD( file->block_list, hbin ); + } + +done: + /* set the offset to be ready to write */ + + if ( !prs_set_offset( &hbin->ps, hbin->free_off-sizeof(uint32) ) ) + return NULL; + + /* write the record size as a placeholder for now, it should be + probably updated by the caller once it all of the data necessary + for the record */ + + if ( !prs_uint32("allocated_size", &hbin->ps, 0, &size) ) + return False; + + update_free_space( hbin, size ); + + return hbin; +} + +/******************************************************************* +*******************************************************************/ + +static uint32 sk_record_data_size( SEC_DESC * sd ) +{ + uint32 size, size_mod8; + + size_mod8 = 0; + + /* the record size is sizeof(hdr) + name + static members + data_size_field */ + + size = sizeof(uint32)*5 + ndr_size_security_descriptor(sd, 0) + sizeof(uint32); + + /* multiple of 8 */ + size_mod8 = size & 0xfffffff8; + if ( size_mod8 < size ) + size_mod8 += 8; + + return size_mod8; +} + +/******************************************************************* +*******************************************************************/ + +static uint32 vk_record_data_size( REGF_VK_REC *vk ) +{ + uint32 size, size_mod8; + + size_mod8 = 0; + + /* the record size is sizeof(hdr) + name + static members + data_size_field */ + + size = REC_HDR_SIZE + (sizeof(uint16)*3) + (sizeof(uint32)*3) + sizeof(uint32); + + if ( vk->valuename ) + size += strlen(vk->valuename); + + /* multiple of 8 */ + size_mod8 = size & 0xfffffff8; + if ( size_mod8 < size ) + size_mod8 += 8; + + return size_mod8; +} + +/******************************************************************* +*******************************************************************/ + +static uint32 lf_record_data_size( uint32 num_keys ) +{ + uint32 size, size_mod8; + + size_mod8 = 0; + + /* the record size is sizeof(hdr) + num_keys + sizeof of hash_array + data_size_uint32 */ + + size = REC_HDR_SIZE + sizeof(uint16) + (sizeof(REGF_HASH_REC) * num_keys) + sizeof(uint32); + + /* multiple of 8 */ + size_mod8 = size & 0xfffffff8; + if ( size_mod8 < size ) + size_mod8 += 8; + + return size_mod8; +} + +/******************************************************************* +*******************************************************************/ + +static uint32 nk_record_data_size( REGF_NK_REC *nk ) +{ + uint32 size, size_mod8; + + size_mod8 = 0; + + /* the record size is static + length_of_keyname + length_of_classname + data_size_uint32 */ + + size = 0x4c + strlen(nk->keyname) + sizeof(uint32); + + if ( nk->classname ) + size += strlen( nk->classname ); + + /* multiple of 8 */ + size_mod8 = size & 0xfffffff8; + if ( size_mod8 < size ) + size_mod8 += 8; + + return size_mod8; +} + +/******************************************************************* +*******************************************************************/ + +static bool create_vk_record( REGF_FILE *file, REGF_VK_REC *vk, REGISTRY_VALUE *value ) +{ + char *name = regval_name(value); + REGF_HBIN *data_hbin; + + ZERO_STRUCTP( vk ); + + memcpy( vk->header, "vk", REC_HDR_SIZE ); + + if ( name ) { + vk->valuename = talloc_strdup( file->mem_ctx, regval_name(value) ); + vk->flag = VK_FLAG_NAME_PRESENT; + } + + vk->data_size = regval_size( value ); + vk->type = regval_type( value ); + + if ( vk->data_size > sizeof(uint32) ) { + uint32 data_size = ( (vk->data_size+sizeof(uint32)) & 0xfffffff8 ) + 8; + + vk->data = (uint8 *)TALLOC_MEMDUP( file->mem_ctx, + regval_data_p(value), + vk->data_size ); + if (vk->data == NULL) { + return False; + } + + /* go ahead and store the offset....we'll pick this hbin block back up when + we stream the data */ + + if ((data_hbin = find_free_space(file, data_size )) == NULL) { + return False; + } + vk->data_off = prs_offset( &data_hbin->ps ) + data_hbin->first_hbin_off - HBIN_HDR_SIZE; + } + else { + /* make sure we don't try to copy from a NULL value pointer */ + + if ( vk->data_size != 0 ) + memcpy( &vk->data_off, regval_data_p(value), sizeof(uint32) ); + vk->data_size |= VK_DATA_IN_OFFSET; + } + + return True; +} + +/******************************************************************* +*******************************************************************/ + +static int hashrec_cmp( REGF_HASH_REC *h1, REGF_HASH_REC *h2 ) +{ + return StrCaseCmp( h1->fullname, h2->fullname ); +} + +/******************************************************************* +*******************************************************************/ + + REGF_NK_REC* regfio_write_key( REGF_FILE *file, const char *name, + REGVAL_CTR *values, REGSUBKEY_CTR *subkeys, + SEC_DESC *sec_desc, REGF_NK_REC *parent ) +{ + REGF_NK_REC *nk; + REGF_HBIN *vlist_hbin = NULL; + uint32 size; + + if ( !(nk = TALLOC_ZERO_P( file->mem_ctx, REGF_NK_REC )) ) + return NULL; + + memcpy( nk->header, "nk", REC_HDR_SIZE ); + + if ( !parent ) + nk->key_type = NK_TYPE_ROOTKEY; + else + nk->key_type = NK_TYPE_NORMALKEY; + + /* store the parent offset (or -1 if a the root key */ + + nk->parent_off = parent ? (parent->hbin_off + parent->hbin->file_off - REGF_BLOCKSIZE - HBIN_HDR_SIZE ) : REGF_OFFSET_NONE; + + /* no classname currently */ + + nk->classname_off = REGF_OFFSET_NONE; + nk->classname = NULL; + nk->keyname = talloc_strdup( file->mem_ctx, name ); + + /* current modification time */ + + unix_to_nt_time( &nk->mtime, time(NULL) ); + + /* allocate the record on disk */ + + size = nk_record_data_size( nk ); + nk->rec_size = ( size - 1 ) ^ 0XFFFFFFFF; + if ((nk->hbin = find_free_space( file, size )) == NULL) { + return NULL; + } + nk->hbin_off = prs_offset( &nk->hbin->ps ); + + /* Update the hash record in the parent */ + + if ( parent ) { + REGF_HASH_REC *hash = &parent->subkeys.hashes[parent->subkey_index]; + + hash->nk_off = prs_offset( &nk->hbin->ps ) + nk->hbin->first_hbin_off - HBIN_HDR_SIZE; + memcpy( hash->keycheck, name, sizeof(uint32) ); + hash->fullname = talloc_strdup( file->mem_ctx, name ); + parent->subkey_index++; + + /* sort the list by keyname */ + + qsort( parent->subkeys.hashes, parent->subkey_index, sizeof(REGF_HASH_REC), QSORT_CAST hashrec_cmp ); + + if ( !hbin_prs_lf_records( "lf_rec", parent->subkeys.hbin, 0, parent ) ) + return False; + } + + /* write the security descriptor */ + + nk->sk_off = REGF_OFFSET_NONE; + if ( sec_desc ) { + uint32 sk_size = sk_record_data_size( sec_desc ); + REGF_HBIN *sk_hbin; + + /* search for it in the existing list of sd's */ + + if ( (nk->sec_desc = find_sk_record_by_sec_desc( file, sec_desc )) == NULL ) { + /* not found so add it to the list */ + + if (!(sk_hbin = find_free_space( file, sk_size ))) { + return NULL; + } + + if ( !(nk->sec_desc = TALLOC_ZERO_P( file->mem_ctx, REGF_SK_REC )) ) + return NULL; + + /* now we have to store the security descriptor in the list and + update the offsets */ + + memcpy( nk->sec_desc->header, "sk", REC_HDR_SIZE ); + nk->sec_desc->hbin = sk_hbin; + nk->sec_desc->hbin_off = prs_offset( &sk_hbin->ps ); + nk->sec_desc->sk_off = prs_offset( &sk_hbin->ps ) + sk_hbin->first_hbin_off - HBIN_HDR_SIZE; + nk->sec_desc->rec_size = (sk_size-1) ^ 0xFFFFFFFF; + + nk->sec_desc->sec_desc = sec_desc; + nk->sec_desc->ref_count = 0; + + /* size value must be self-inclusive */ + nk->sec_desc->size = ndr_size_security_descriptor(sec_desc, 0) + + sizeof(uint32); + + DLIST_ADD_END( file->sec_desc_list, nk->sec_desc, REGF_SK_REC *); + + /* update the offsets for us and the previous sd in the list. + if this is the first record, then just set the next and prev + offsets to ourself. */ + + if ( nk->sec_desc->prev ) { + REGF_SK_REC *prev = nk->sec_desc->prev; + + nk->sec_desc->prev_sk_off = prev->hbin_off + prev->hbin->first_hbin_off - HBIN_HDR_SIZE; + prev->next_sk_off = nk->sec_desc->sk_off; + + /* the end must loop around to the front */ + nk->sec_desc->next_sk_off = file->sec_desc_list->sk_off; + + /* and first must loop around to the tail */ + file->sec_desc_list->prev_sk_off = nk->sec_desc->sk_off; + } else { + nk->sec_desc->prev_sk_off = nk->sec_desc->sk_off; + nk->sec_desc->next_sk_off = nk->sec_desc->sk_off; + } + } + + /* bump the reference count +1 */ + + nk->sk_off = nk->sec_desc->sk_off; + nk->sec_desc->ref_count++; + } + + /* write the subkeys */ + + nk->subkeys_off = REGF_OFFSET_NONE; + if ( (nk->num_subkeys = regsubkey_ctr_numkeys( subkeys )) != 0 ) { + uint32 lf_size = lf_record_data_size( nk->num_subkeys ); + uint32 namelen; + int i; + + if (!(nk->subkeys.hbin = find_free_space( file, lf_size ))) { + return NULL; + } + nk->subkeys.hbin_off = prs_offset( &nk->subkeys.hbin->ps ); + nk->subkeys.rec_size = (lf_size-1) ^ 0xFFFFFFFF; + nk->subkeys_off = prs_offset( &nk->subkeys.hbin->ps ) + nk->subkeys.hbin->first_hbin_off - HBIN_HDR_SIZE; + + memcpy( nk->subkeys.header, "lf", REC_HDR_SIZE ); + + nk->subkeys.num_keys = nk->num_subkeys; + if (nk->subkeys.num_keys) { + if ( !(nk->subkeys.hashes = TALLOC_ZERO_ARRAY( file->mem_ctx, REGF_HASH_REC, nk->subkeys.num_keys )) ) + return NULL; + } else { + nk->subkeys.hashes = NULL; + } + nk->subkey_index = 0; + + /* update the max_bytes_subkey{name,classname} fields */ + for ( i=0; i<nk->num_subkeys; i++ ) { + namelen = strlen( regsubkey_ctr_specific_key(subkeys, i) ); + if ( namelen*2 > nk->max_bytes_subkeyname ) + nk->max_bytes_subkeyname = namelen * 2; + } + } + + /* write the values */ + + nk->values_off = REGF_OFFSET_NONE; + if ( (nk->num_values = regval_ctr_numvals( values )) != 0 ) { + uint32 vlist_size = ( ( nk->num_values * sizeof(uint32) ) & 0xfffffff8 ) + 8; + int i; + + if (!(vlist_hbin = find_free_space( file, vlist_size ))) { + return NULL; + } + nk->values_off = prs_offset( &vlist_hbin->ps ) + vlist_hbin->first_hbin_off - HBIN_HDR_SIZE; + + if (nk->num_values) { + if ( !(nk->values = TALLOC_ARRAY( file->mem_ctx, REGF_VK_REC, nk->num_values )) ) + return NULL; + } else { + nk->values = NULL; + } + + /* create the vk records */ + + for ( i=0; i<nk->num_values; i++ ) { + uint32 vk_size, namelen, datalen; + REGISTRY_VALUE *r; + + r = regval_ctr_specific_value( values, i ); + create_vk_record( file, &nk->values[i], r ); + vk_size = vk_record_data_size( &nk->values[i] ); + nk->values[i].hbin = find_free_space( file, vk_size ); + nk->values[i].hbin_off = prs_offset( &nk->values[i].hbin->ps ); + nk->values[i].rec_size = ( vk_size - 1 ) ^ 0xFFFFFFFF; + nk->values[i].rec_off = prs_offset( &nk->values[i].hbin->ps ) + + nk->values[i].hbin->first_hbin_off + - HBIN_HDR_SIZE; + + /* update the max bytes fields if necessary */ + + namelen = strlen( regval_name(r) ); + if ( namelen*2 > nk->max_bytes_valuename ) + nk->max_bytes_valuename = namelen * 2; + + datalen = regval_size( r ); + if ( datalen > nk->max_bytes_value ) + nk->max_bytes_value = datalen; + } + } + + /* stream the records */ + + prs_set_offset( &nk->hbin->ps, nk->hbin_off ); + if ( !prs_nk_rec( "nk_rec", &nk->hbin->ps, 0, nk ) ) + return False; + + if ( nk->num_values ) { + if ( !hbin_prs_vk_records( "vk_records", vlist_hbin, 0, nk, file ) ) + return False; + } + + + regfio_flush( file ); + + return nk; +} + |