1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00

r10020: Support for malloc / realloc / free in HBIN blocks

Initial support for adding keys (still broken though)
(This used to be commit ff8d3a27c1)
This commit is contained in:
Jelmer Vernooij 2005-09-04 02:09:32 +00:00 committed by Gerald (Jerry) Carter
parent 16788f27a8
commit 23e6e07b28
3 changed files with 333 additions and 25 deletions

View File

@ -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);

View File

@ -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)

View File

@ -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;
/*