summaryrefslogtreecommitdiff
path: root/source4/lib
diff options
context:
space:
mode:
Diffstat (limited to 'source4/lib')
-rw-r--r--source4/lib/registry/common/reg_interface.c5
-rw-r--r--source4/lib/registry/reg_backend_nt4.c340
-rw-r--r--source4/lib/registry/regf.idl13
3 files changed, 333 insertions, 25 deletions
diff --git a/source4/lib/registry/common/reg_interface.c b/source4/lib/registry/common/reg_interface.c
index 5297b1b3cf..ddd4ecbe93 100644
--- a/source4/lib/registry/common/reg_interface.c
+++ b/source4/lib/registry/common/reg_interface.c
@@ -409,6 +409,11 @@ WERROR reg_key_add_name(TALLOC_CTX *mem_ctx, struct registry_key *parent, const
error = parent->hive->functions->add_key(mem_ctx, parent, name, access_mask, desc, newkey);
if(!W_ERROR_IS_OK(error)) return error;
+
+ if (!*newkey) {
+ DEBUG(0, ("Backend returned WERR_OK, but didn't specify key!\n"));
+ return WERR_GENERAL_FAILURE;
+ }
(*newkey)->hive = parent->hive;
(*newkey)->backend_data = talloc_asprintf(mem_ctx, "%s\\%s", parent->path, name);
diff --git a/source4/lib/registry/reg_backend_nt4.c b/source4/lib/registry/reg_backend_nt4.c
index b1c0d201f0..46057bcd63 100644
--- a/source4/lib/registry/reg_backend_nt4.c
+++ b/source4/lib/registry/reg_backend_nt4.c
@@ -20,6 +20,7 @@
#include "includes.h"
#include "registry.h"
#include "system/filesys.h"
+#include "system/time.h"
#include "lib/registry/tdr_regf.h"
#include "librpc/gen_ndr/ndr_security.h"
@@ -32,6 +33,23 @@ struct regf_data {
struct hbin_block **hbins;
};
+static struct hbin_block *hbin_by_offset (const struct regf_data *data, uint32_t offset, uint32_t *rel_offset)
+{
+ int i;
+
+ for (i = 0; data->hbins[i]; i++) {
+ if (offset >= data->hbins[i]->offset_from_first &&
+ offset < data->hbins[i]->offset_from_first+
+ data->hbins[i]->offset_to_next) {
+ if (rel_offset)
+ *rel_offset = offset - data->hbins[i]->offset_from_first - 0x20;
+ return data->hbins[i];
+ }
+ }
+
+ return NULL;
+}
+
/*
* Validate a regf header
* For now, do nothing, but we should check the checksum
@@ -49,37 +67,216 @@ static uint32_t regf_hdr_checksum(const uint8_t *buffer)
return checksum;
}
-static DATA_BLOB regf_get_data(const struct regf_data *data, uint32_t offset)
+static DATA_BLOB hbin_get_data(const struct regf_data *data, uint32_t offset)
{
- int i;
DATA_BLOB ret;
+ struct hbin_block *hbin;
+ uint32_t rel_offset;
ret.data = NULL;
ret.length = 0;
-
- for (i = 0; data->hbins[i]; i++) {
- if (offset >= data->hbins[i]->offset_from_first &&
- offset < data->hbins[i]->offset_from_first+
- data->hbins[i]->offset_to_next)
- break;
- }
- if (data->hbins[i] == NULL) {
+ hbin = hbin_by_offset(data, offset, &rel_offset);
+
+ if (hbin == NULL) {
DEBUG(1, ("Can't find HBIN containing 0x%4x\n", offset));
return ret;
}
- ret.length = IVAL(data->hbins[i]->data,
- offset - data->hbins[i]->offset_from_first - 0x20);
+ ret.length = IVAL(hbin->data, rel_offset);
if (ret.length & 0x80000000) {
/* absolute value */
ret.length = (ret.length ^ 0xffffffff) + 1;
}
- ret.data = data->hbins[i]->data +
- (offset - data->hbins[i]->offset_from_first - 0x20) + 4;
+ ret.length -= 4; /* 4 bytes for the length... */
+ ret.data = hbin->data +
+ (offset - hbin->offset_from_first - 0x20) + 4;
+
+ return ret;
+}
+
+/* Allocate some new data */
+static DATA_BLOB hbin_alloc (struct regf_data *data, uint32_t size, uint32_t *offset)
+{
+ DATA_BLOB ret;
+ uint32_t rel_offset = -1; /* Relative offset ! */
+ struct hbin_block *hbin = NULL;
+ int i;
+
+ size += 4; /* Need to include uint32 for the length */
+
+ /* Allocate as a multiple of 8 */
+ size = (size + 7) & ~7;
+
+ ret.data = NULL;
+ ret.length = 0;
+
+ if (size == 0)
+ return ret;
+
+ for (i = 0; (hbin = data->hbins[i]); i++) {
+ int j;
+ uint32_t my_size;
+ for (j = 0; j < hbin->offset_to_next-0x20; j+= my_size) {
+ my_size = IVAL(hbin->data, j);
+ uint32_t header = IVAL(hbin->data, j + 4);
+
+ if (my_size % 8 != 0) {
+ DEBUG(0, ("Encountered non-aligned block!\n"));
+ }
+
+ if (my_size & 0x80000000) { /* Used... */
+ my_size = (my_size ^ 0xffffffff) + 1;
+ } else if (my_size == size) { /* exact match */
+ rel_offset = j;
+ break;
+ } else if (my_size > size) { /* data will remain */
+ rel_offset = j;
+ SIVAL(hbin->data, rel_offset+size, my_size-size);
+ break;
+ }
+
+ if (header == 0xffffffff &&
+ hbin->offset_to_next-rel_offset >= size) {
+ rel_offset = j;
+ /* Mark new free block size */
+ SIVAL(hbin->data, rel_offset+size,hbin->offset_to_next - rel_offset - size - 0x20);
+ SIVAL(hbin->data, rel_offset+size+0x4, 0xffffffff);
+ break;
+ }
+
+ if (header == 0xffffffff) {
+ break;
+ }
+ }
+
+ if (rel_offset != -1)
+ break;
+ }
+ /* No space available in previous hbins,
+ * allocate new one */
+ if (data->hbins[i] == NULL) {
+ data->hbins = talloc_realloc(data, data->hbins, struct hbin_block *, i+2);
+ hbin = talloc(data->hbins, struct hbin_block);
+ data->hbins[i] = hbin;
+ data->hbins[i+1] = NULL;
+
+ hbin->HBIN_ID = talloc_strdup(hbin, "hbin");
+ hbin->offset_from_first = (i == 0?0:data->hbins[i-1]->offset_from_first+data->hbins[i-1]->offset_to_next);
+ hbin->offset_to_next = 0x1000;
+ hbin->unknown[0] = 0;
+ hbin->unknown[0] = 0;
+ unix_to_nt_time(&hbin->last_change, time(NULL));
+ hbin->block_size = hbin->offset_to_next;
+ hbin->data = talloc_zero_array(hbin, uint8_t, hbin->block_size - 0x20);
+
+ rel_offset = 0x0;
+ SIVAL(hbin->data, size, hbin->block_size - size - 0x20);
+ SIVAL(hbin->data, size + 0x4, 0xffffffff);
+ }
+
+ /* Set size and mark as used */
+ SIVAL(hbin->data, rel_offset, size & 0x80000000);
+
+ ret.data = hbin->data + rel_offset + 0x4; /* Skip past length */
+ ret.length = size - 0x4;
+ if (offset)
+ *offset = hbin->offset_from_first + rel_offset + 0x20;
+
return ret;
}
+/* Store a data blob. Return the offset at which it was stored */
+static uint32_t hbin_store (struct regf_data *data, DATA_BLOB blob)
+{
+ uint32_t ret;
+ DATA_BLOB dest = hbin_alloc(data, blob.length, &ret);
+
+ memcpy(dest.data, blob.data, blob.length);
+
+ return ret;
+}
+
+
+/* Free existing data */
+static void hbin_free (struct regf_data *data, uint32_t offset)
+{
+ uint32_t size;
+ uint32_t rel_offset;
+ struct hbin_block *hbin = hbin_by_offset(data, offset, &rel_offset);
+
+ if (hbin == NULL)
+ return;
+
+ /* Get original size */
+ size = IVAL(hbin->data, rel_offset);
+
+ if (!(size & 0x80000000)) {
+ DEBUG(1, ("Trying to free already freed block\n"));
+ return;
+ }
+
+ /* Mark block as free */
+ SIVAL(hbin->data, rel_offset, (size ^ 0xffffffff) + 1);
+}
+
+/* Store a data blob data was already stored, but hsa changed in size
+ * Will try to save it at the current location if possible, otherwise
+ * does a free + store */
+static uint32_t hbin_store_resize (struct regf_data *data, uint32_t orig_offset, DATA_BLOB blob)
+{
+ uint32_t rel_offset;
+ struct hbin_block *hbin = hbin_by_offset(data, orig_offset, &rel_offset);
+ uint32_t orig_size;
+ uint32_t needed_size;
+ uint32_t possible_size;
+ int i;
+
+ if (!hbin)
+ return hbin_store(data, blob);
+
+ /* Get original size */
+ orig_size = IVAL(hbin->data, rel_offset);
+
+ /* Fits into current allocated block */
+ if (orig_size - 4 >= blob.length) {
+ memcpy(hbin->data + rel_offset + 0x4, blob.data, blob.length);
+ return orig_offset;
+ }
+
+ needed_size = blob.length + 4; /* Add uint32 containing length */
+ needed_size = (needed_size + 7) & ~7; /* Align */
+
+ possible_size = orig_size;
+
+ /* Check if it can be combined with the next few free records */
+ for (i = rel_offset;
+ i < hbin->offset_to_next - 0x20;
+ i = rel_offset + possible_size) {
+ uint32_t header;
+ if (IVAL(hbin->data, i) & 0x80000000) /* Used */
+ break;
+
+ header = IVAL(hbin->data, i + 4);
+ if (header == 0xffffffff) {
+ possible_size = hbin->offset_to_next - 0x20 - rel_offset;
+ } else {
+ possible_size += IVAL(hbin->data, i);
+ }
+
+ if (possible_size >= blob.length) {
+ SIVAL(hbin->data, rel_offset, possible_size);
+ memcpy(hbin->data + rel_offset + 0x4, blob.data, blob.length);
+ return orig_offset;
+ }
+
+ if (header == 0xffffffff)
+ break;
+ }
+
+ hbin_free(data, orig_offset);
+ return hbin_store(data, blob);
+}
static WERROR regf_num_subkeys (struct registry_key *key, uint32_t *count)
{
@@ -101,7 +298,7 @@ static WERROR regf_num_values (struct registry_key *key, uint32_t *count)
static struct registry_key *regf_get_key (TALLOC_CTX *ctx, struct regf_data *regf, uint32_t offset)
{
- DATA_BLOB data = regf_get_data(regf, offset);
+ DATA_BLOB data = hbin_get_data(regf, offset);
struct tdr_pull *pull;
struct registry_key *ret;
struct nk_block *nk;
@@ -147,7 +344,7 @@ static WERROR regf_get_value (TALLOC_CTX *ctx, struct registry_key *key, int idx
if (idx >= nk->num_values)
return WERR_NO_MORE_ITEMS;
- data = regf_get_data(key->hive->backend_data, nk->values_offset);
+ data = hbin_get_data(key->hive->backend_data, nk->values_offset);
if (!data.data) {
DEBUG(0, ("Unable to find value list\n"));
return WERR_GENERAL_FAILURE;
@@ -159,7 +356,7 @@ static WERROR regf_get_value (TALLOC_CTX *ctx, struct registry_key *key, int idx
vk_offset = IVAL(data.data, idx * 4);
- data = regf_get_data(key->hive->backend_data, vk_offset);
+ data = hbin_get_data(key->hive->backend_data, vk_offset);
if (!data.data) {
DEBUG(0, ("Unable to find value\n"));
return WERR_GENERAL_FAILURE;
@@ -188,7 +385,7 @@ static WERROR regf_get_value (TALLOC_CTX *ctx, struct registry_key *key, int idx
(*ret)->data.data = (uint8_t *)&vk->data_offset;
(*ret)->data.length = vk->data_length;
} else {
- (*ret)->data = regf_get_data(key->hive->backend_data, vk->data_offset);
+ (*ret)->data = hbin_get_data(key->hive->backend_data, vk->data_offset);
}
if ((*ret)->data.length < vk->data_length) {
@@ -207,7 +404,7 @@ static WERROR regf_get_subkey (TALLOC_CTX *ctx, struct registry_key *key, int id
if (idx >= nk->num_subkeys)
return WERR_NO_MORE_ITEMS;
- data = regf_get_data(key->hive->backend_data, nk->subkeys_offset);
+ data = hbin_get_data(key->hive->backend_data, nk->subkeys_offset);
if (!data.data) {
DEBUG(0, ("Unable to find subkey list\n"));
return WERR_GENERAL_FAILURE;
@@ -259,7 +456,7 @@ static WERROR regf_get_sec_desc(TALLOC_CTX *ctx, struct registry_key *key, struc
struct sk_block sk;
DATA_BLOB data;
- data = regf_get_data(key->hive->backend_data, nk->sk_offset);
+ data = hbin_get_data(key->hive->backend_data, nk->sk_offset);
if (!data.data) {
DEBUG(0, ("Unable to find security descriptor\n"));
return WERR_GENERAL_FAILURE;
@@ -297,6 +494,108 @@ static WERROR regf_get_sec_desc(TALLOC_CTX *ctx, struct registry_key *key, struc
return WERR_OK;
}
+static uint32_t lf_add_entry (struct regf_data *regf, uint32_t list_offset, const char *name, uint32_t key_offset)
+{
+ uint32_t ret;
+ struct lf_block lf;
+ struct tdr_pull *pull = NULL;
+ struct tdr_push *push;
+
+ /* Add to subkeys list */
+ if (list_offset == -1) { /* Need to create subkeys list */
+ lf.header = "lf";
+ lf.key_count = 0;
+ lf.hr = NULL;
+ } else {
+ DATA_BLOB data;
+ pull = talloc(regf, struct tdr_pull);
+
+ data = hbin_get_data(regf, list_offset);
+ if (!data.data) {
+ DEBUG(0, ("Can't get subkeys list\n"));
+ talloc_free(pull);
+ return -1;
+ }
+
+ if (NT_STATUS_IS_ERR(tdr_pull_lf_block(pull, &lf))) {
+ DEBUG(0, ("Unable to parse lf list\n"));
+ talloc_free(pull);
+ return -1;
+ }
+ }
+
+ lf.hr = talloc_realloc(pull, lf.hr, struct hash_record, lf.key_count+1);
+ lf.hr[lf.key_count].nk_off = key_offset;
+ lf.hr[lf.key_count].hash = name;
+
+ push = talloc_zero(regf, struct tdr_push);
+
+ if (NT_STATUS_IS_ERR(tdr_push_lf_block(push, &lf))) {
+ DEBUG(0, ("Error storing lf block\n"));
+ return -1;
+ }
+
+ ret = hbin_store_resize (regf, list_offset, push->data);
+
+ talloc_free(push);
+ talloc_free(pull);
+
+ return ret;
+}
+
+static WERROR regf_add_key (TALLOC_CTX *ctx, struct registry_key *parent, const char *name, uint32_t access_mask, struct security_descriptor *sec_desc, struct registry_key **ret)
+{
+ struct nk_block *parent_nk = parent->backend_data, nk;
+ DATA_BLOB data;
+ struct tdr_push *push;
+ uint32_t offset;
+
+ nk.header = "nk";
+ nk.type = REG_SUB_KEY;
+ unix_to_nt_time(&nk.last_change, time(NULL));
+ nk.uk1 = 0;
+ nk.parent_offset = 0; /* FIXME */
+ nk.num_subkeys = 0;
+ nk.uk2 = 0;
+ nk.subkeys_offset = -1;
+ nk.unknown_offset = -1;
+ nk.num_values = 0;
+ nk.sk_offset = 0;
+ memset(nk.unk3, 0, 5);
+ nk.clsname_offset = -1;
+ nk.clsname_length = 0;
+ nk.key_name = name;
+
+ push = talloc_zero(ctx, struct tdr_push);
+ if (NT_STATUS_IS_ERR(tdr_push_nk_block(push, &nk))) {
+ DEBUG(0, ("Error storing 'nk' block\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ offset = hbin_store(parent->hive->backend_data, push->data);
+
+ parent_nk->subkeys_offset = lf_add_entry(parent->hive->backend_data, parent_nk->subkeys_offset, name, nk.parent_offset);
+
+ parent_nk->num_subkeys++;
+
+ ZERO_STRUCTP(push);
+
+ if (NT_STATUS_IS_ERR(tdr_push_nk_block(push, parent_nk))) {
+ DEBUG(0, ("Error storing parent 'nk' block\n"));
+ return WERR_GENERAL_FAILURE;
+ }
+
+ data = hbin_get_data(parent->hive->backend_data, nk.parent_offset);
+ memcpy(data.data, push->data.data, push->data.length);
+
+ talloc_free(push);
+
+ /* FIXME: Set sec desc ! */
+
+ *ret = regf_get_key(ctx, parent->hive->backend_data, offset);
+ return WERR_OK;
+}
+
static WERROR nt_open_hive (struct registry_hive *h, struct registry_key **key)
{
struct regf_data *regf;
@@ -389,6 +688,7 @@ static struct hive_operations reg_backend_nt4 = {
.get_subkey_by_index = regf_get_subkey,
.get_value_by_index = regf_get_value,
.key_get_sec_desc = regf_get_sec_desc,
+ .add_key = regf_add_key,
};
NTSTATUS registry_nt4_init(void)
diff --git a/source4/lib/registry/regf.idl b/source4/lib/registry/regf.idl
index 3b0a66fc54..85a879cc36 100644
--- a/source4/lib/registry/regf.idl
+++ b/source4/lib/registry/regf.idl
@@ -64,8 +64,11 @@ interface regf
uint32 block_size; /* Block size (including the header!) */
uint8 data[offset_to_next-0x20];
/* data is filled with:
- uint32 length
- uint8_t data[length]
+ uint32 length;
+ Negative if in used, positive otherwise
+ Always a multiple of 8
+ uint8_t data[length];
+ Free space marker if 0xffffffff
*/
} hbin_block;
@@ -94,7 +97,7 @@ interface regf
uint32 sk_offset;
uint32 clsname_offset;
uint32 unk3[5];
- uint16 name_length;
+ [value(strlen(key_name))] uint16 name_length;
uint16 clsname_length;
[charset(DOS)] uint8 key_name[name_length];
} nk_block;
@@ -137,7 +140,7 @@ interface regf
/* The vk-record consists information to a single value (value key). */
typedef [public,noprint] struct {
[charset(DOS)] uint8 header[2];
- uint16 name_length;
+ [value(strlen(data_name))] uint16 name_length;
uint32 data_length; /* If top-bit set, offset contains the data */
uint32 data_offset;
uint32 data_type;
@@ -148,7 +151,7 @@ interface regf
typedef [noprint] struct {
uint32 nk_off;
- uint8 hash[4];
+ [charset(DOS)] uint8 hash[4];
} hash_record;
/*