diff options
Diffstat (limited to 'source3/registry/reg_api.c')
-rw-r--r-- | source3/registry/reg_api.c | 1247 |
1 files changed, 1247 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 */ |