2 Samba CIFS implementation
3 Registry backend for REGF files
4 Copyright (C) 2005 Jelmer Vernooij, jelmer@samba.org
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
22 #include "system/filesys.h"
23 #include "system/time.h"
24 #include "lib/registry/tdr_regf.h"
25 #include "librpc/gen_ndr/ndr_security.h"
28 * - Return error codes that make more sense
33 * Read HBIN blocks into memory
38 struct hbin_block **hbins;
39 struct regf_hdr *header;
42 static struct hbin_block *hbin_by_offset (const struct regf_data *data, uint32_t offset, uint32_t *rel_offset)
46 for (i = 0; data->hbins[i]; i++) {
47 if (offset >= data->hbins[i]->offset_from_first &&
48 offset < data->hbins[i]->offset_from_first+
49 data->hbins[i]->offset_to_next) {
51 *rel_offset = offset - data->hbins[i]->offset_from_first - 0x20;
52 return data->hbins[i];
60 * Validate a regf header
61 * For now, do nothing, but we should check the checksum
63 static uint32_t regf_hdr_checksum(const uint8_t *buffer)
65 uint32_t checksum = 0, x;
68 for (i = 0; i < 0x01FB; i+= 4) {
76 static DATA_BLOB hbin_get(const struct regf_data *data, uint32_t offset)
79 struct hbin_block *hbin;
84 hbin = hbin_by_offset(data, offset, &rel_offset);
87 DEBUG(1, ("Can't find HBIN containing 0x%04x\n", offset));
91 ret.length = IVAL(hbin->data, rel_offset);
92 if (!(ret.length & 0x80000000)) {
93 DEBUG(0, ("Trying to use dirty block at 0x%04x\n", offset));
98 ret.length = (ret.length ^ 0xffffffff) + 1;
100 ret.length -= 4; /* 4 bytes for the length... */
101 ret.data = hbin->data +
102 (offset - hbin->offset_from_first - 0x20) + 4;
107 static BOOL hbin_get_tdr (struct regf_data *regf, uint32_t offset, TALLOC_CTX *ctx, tdr_pull_fn_t pull_fn, void *p)
109 struct tdr_pull pull;
113 pull.data = hbin_get(regf, offset);
114 if (!pull.data.data) {
115 DEBUG(1, ("Unable to get data at 0x%04x\n", offset));
119 if (NT_STATUS_IS_ERR(pull_fn(&pull, ctx, p))) {
120 DEBUG(1, ("Error parsing record at 0x%04x using tdr\n", offset));
127 /* Allocate some new data */
128 static DATA_BLOB hbin_alloc (struct regf_data *data, uint32_t size, uint32_t *offset)
131 uint32_t rel_offset = -1; /* Relative offset ! */
132 struct hbin_block *hbin = NULL;
138 size += 4; /* Need to include uint32 for the length */
140 /* Allocate as a multiple of 8 */
141 size = (size + 7) & ~7;
146 for (i = 0; (hbin = data->hbins[i]); i++) {
149 for (j = 0; j < hbin->offset_to_next-0x20; j+= my_size) {
150 my_size = IVAL(hbin->data, j);
151 uint32_t header = IVAL(hbin->data, j + 4);
153 if (my_size == 0x0) {
154 DEBUG(0, ("Invalid zero-length block! File is corrupt.\n"));
158 if (my_size % 8 != 0) {
159 DEBUG(0, ("Encountered non-aligned block!\n"));
162 if (my_size & 0x80000000) { /* Used... */
163 my_size = (my_size ^ 0xffffffff) + 1;
164 } else if (my_size == size) { /* exact match */
166 DEBUG(4, ("Found free block of exact size %d in middle of HBIN\n", size));
168 } else if (my_size > size) { /* data will remain */
170 SIVAL(hbin->data, rel_offset+size, my_size-size);
171 DEBUG(4, ("Found free block of size %d (needing %d) in middle of HBIN\n", my_size, size));
175 if (header == 0xffffffff &&
176 hbin->offset_to_next-rel_offset >= size) {
179 DEBUG(4, ("Found free block of size %d at end of HBIN\n", size));
180 /* Mark new free block size */
181 SIVAL(hbin->data, rel_offset+size,hbin->offset_to_next - rel_offset - size - 0x20);
182 SIVAL(hbin->data, rel_offset+size+0x4, 0xffffffff);
186 if (header == 0xffffffff) {
191 if (rel_offset != -1)
195 /* No space available in previous hbins,
196 * allocate new one */
197 if (data->hbins[i] == NULL) {
198 DEBUG(4, ("No space available in other HBINs for block of size %d, allocating new HBIN\n", size));
199 data->hbins = talloc_realloc(data, data->hbins, struct hbin_block *, i+2);
200 hbin = talloc(data->hbins, struct hbin_block);
201 data->hbins[i] = hbin;
202 data->hbins[i+1] = NULL;
204 hbin->HBIN_ID = talloc_strdup(hbin, "hbin");
205 hbin->offset_from_first = (i == 0?0:data->hbins[i-1]->offset_from_first+data->hbins[i-1]->offset_to_next);
206 hbin->offset_to_next = 0x1000;
207 hbin->unknown[0] = 0;
208 hbin->unknown[0] = 0;
209 unix_to_nt_time(&hbin->last_change, time(NULL));
210 hbin->block_size = hbin->offset_to_next;
211 hbin->data = talloc_zero_array(hbin, uint8_t, hbin->block_size - 0x20);
214 SIVAL(hbin->data, size, hbin->block_size - size - 0x20);
215 SIVAL(hbin->data, size + 0x4, 0xffffffff);
218 /* Set size and mark as used */
219 SIVAL(hbin->data, rel_offset, size | 0x80000000);
221 ret.data = hbin->data + rel_offset + 0x4; /* Skip past length */
222 ret.length = size - 0x4;
224 uint32_t new_rel_offset;
225 *offset = hbin->offset_from_first + rel_offset + 0x20;
226 SMB_ASSERT(hbin_by_offset(data, *offset, &new_rel_offset) == hbin);
227 SMB_ASSERT(new_rel_offset == rel_offset);
233 /* Store a data blob. Return the offset at which it was stored */
234 static uint32_t hbin_store (struct regf_data *data, DATA_BLOB blob)
237 DATA_BLOB dest = hbin_alloc(data, blob.length, &ret);
239 memcpy(dest.data, blob.data, blob.length);
244 static uint32_t hbin_store_tdr (struct regf_data *data, tdr_push_fn_t push_fn, void *p)
246 struct tdr_push *push = talloc_zero(data, struct tdr_push);
249 if (NT_STATUS_IS_ERR(push_fn(push, p))) {
250 DEBUG(0, ("Error during push\n"));
254 ret = hbin_store(data, push->data);
262 /* Free existing data */
263 static void hbin_free (struct regf_data *data, uint32_t offset)
267 struct hbin_block *hbin;
269 SMB_ASSERT (offset > 0);
271 hbin = hbin_by_offset(data, offset, &rel_offset);
276 /* Get original size */
277 size = IVAL(hbin->data, rel_offset);
279 if (!(size & 0x80000000)) {
280 DEBUG(1, ("Trying to free already freed block at 0x%04x\n", offset));
284 /* Mark block as free */
285 SIVAL(hbin->data, rel_offset, size &~ 0x80000000);
288 /* Store a data blob data was already stored, but hsa changed in size
289 * Will try to save it at the current location if possible, otherwise
290 * does a free + store */
291 static uint32_t hbin_store_resize (struct regf_data *data, uint32_t orig_offset, DATA_BLOB blob)
294 struct hbin_block *hbin = hbin_by_offset(data, orig_offset, &rel_offset);
297 uint32_t needed_size;
298 uint32_t possible_size;
301 SMB_ASSERT(orig_offset > 0);
304 return hbin_store(data, blob);
306 /* Get original size */
307 orig_size = IVAL(hbin->data, rel_offset);
309 needed_size = blob.length + 4; /* Add uint32 containing length */
310 needed_size = (needed_size + 7) & ~7; /* Align */
312 /* Fits into current allocated block */
313 if (orig_size >= needed_size) {
314 memcpy(hbin->data + rel_offset + 0x4, blob.data, blob.length);
318 possible_size = orig_size;
320 /* Check if it can be combined with the next few free records */
322 i < hbin->offset_to_next - 0x20;
325 if (IVAL(hbin->data, i) & 0x80000000) /* Used */
328 my_size = IVAL(hbin->data, i);
329 header = IVAL(hbin->data, i + 4);
330 if (header == 0xffffffff) {
331 possible_size = hbin->offset_to_next - 0x20 - rel_offset;
332 } else if (my_size == 0x0) {
333 DEBUG(0, ("Invalid zero-length block! File is corrupt.\n"));
336 possible_size += my_size;
339 if (possible_size >= blob.length) {
340 SIVAL(hbin->data, rel_offset, possible_size);
341 memcpy(hbin->data + rel_offset + 0x4, blob.data, blob.length);
345 if (header == 0xffffffff)
349 hbin_free(data, orig_offset);
350 return hbin_store(data, blob);
353 static uint32_t hbin_store_tdr_resize (struct regf_data *regf, tdr_push_fn_t push_fn, uint32_t orig_offset, void *p)
355 struct tdr_push *push = talloc_zero(regf, struct tdr_push);
358 if (NT_STATUS_IS_ERR(push_fn(push, p))) {
359 DEBUG(0, ("Error during push\n"));
363 ret = hbin_store_resize(regf, orig_offset, push->data);
370 static WERROR regf_num_subkeys (struct registry_key *key, uint32_t *count)
372 struct nk_block *nk = key->backend_data;
374 *count = nk->num_subkeys;
379 static WERROR regf_num_values (struct registry_key *key, uint32_t *count)
381 struct nk_block *nk = key->backend_data;
383 *count = nk->num_values;
388 static struct registry_key *regf_get_key (TALLOC_CTX *ctx, struct regf_data *regf, uint32_t offset)
390 struct registry_key *ret;
393 ret = talloc_zero(ctx, struct registry_key);
394 nk = talloc(ret, struct nk_block);
395 if (!hbin_get_tdr(regf, offset, nk, (tdr_pull_fn_t)tdr_pull_nk_block, nk)) {
396 DEBUG(0, ("Unable to find HBIN data for offset %d\n", offset));
400 if (strcmp(nk->header, "nk") != 0) {
401 DEBUG(0, ("Expected nk record, got %s\n", nk->header));
406 ret->name = talloc_steal(ret, nk->key_name);
407 ret->last_mod = nk->last_change;
409 if (nk->clsname_offset != -1) {
410 DATA_BLOB data = hbin_get(regf, nk->clsname_offset);
411 ret->class_name = talloc_strndup(ret, (char*)data.data, nk->clsname_length);
413 ret->backend_data = nk;
418 static WERROR regf_get_value (TALLOC_CTX *ctx, struct registry_key *key, int idx, struct registry_value **ret)
420 struct nk_block *nk = key->backend_data;
422 struct regf_data *regf = key->hive->backend_data;
426 if (idx >= nk->num_values)
427 return WERR_NO_MORE_ITEMS;
429 data = hbin_get(regf, nk->values_offset);
431 DEBUG(0, ("Unable to find value list\n"));
432 return WERR_GENERAL_FAILURE;
435 if (data.length < nk->num_values * 4) {
436 DEBUG(1, ("Value counts mismatch\n"));
439 vk_offset = IVAL(data.data, idx * 4);
441 *ret = talloc_zero(ctx, struct registry_value);
445 vk = talloc(*ret, struct vk_block);
449 if (!hbin_get_tdr(regf, vk_offset, vk, (tdr_pull_fn_t)tdr_pull_vk_block, vk)) {
450 DEBUG(0, ("Unable to get VK block at %d\n", vk_offset));
451 return WERR_GENERAL_FAILURE;
454 (*ret)->name = talloc_steal(*ret, vk->data_name);
455 (*ret)->data_type = vk->data_type;
456 if (vk->data_length & 0x80000000) {
457 vk->data_length &=~0x80000000;
458 (*ret)->data.data = (uint8_t *)&vk->data_offset;
459 (*ret)->data.length = vk->data_length;
461 (*ret)->data = hbin_get(regf, vk->data_offset);
464 if ((*ret)->data.length < vk->data_length) {
465 DEBUG(1, ("Read data less then indicated data length!\n"));
471 static WERROR regf_get_subkey (TALLOC_CTX *ctx, struct registry_key *key, int idx, struct registry_key **ret)
474 struct nk_block *nk = key->backend_data;
477 if (idx >= nk->num_subkeys)
478 return WERR_NO_MORE_ITEMS;
480 data = hbin_get(key->hive->backend_data, nk->subkeys_offset);
482 DEBUG(0, ("Unable to find subkey list\n"));
483 return WERR_GENERAL_FAILURE;
486 if (!strncmp((char *)data.data, "li", 2)) {
487 DEBUG(4, ("Subkeys in LI list\n"));
489 } else if (!strncmp((char *)data.data, "lf", 2)) {
491 struct tdr_pull pull;
493 DEBUG(10, ("Subkeys in LF list\n"));
497 if (NT_STATUS_IS_ERR(tdr_pull_lf_block(&pull, nk, &lf))) {
498 DEBUG(0, ("Error parsing LF list\n"));
499 return WERR_GENERAL_FAILURE;
502 if (lf.key_count != nk->num_subkeys) {
503 DEBUG(0, ("Subkey counts don't match\n"));
504 return WERR_GENERAL_FAILURE;
507 key_off = lf.hr[idx].nk_off;
508 } else if (!strncmp((char *)data.data, "ri", 2)) {
509 DEBUG(4, ("Subkeys in RI list\n"));
511 } else if (!strncmp((char *)data.data, "lh", 2)) {
512 DEBUG(4, ("Subkeys in LH list\n"));
515 DEBUG(0, ("Unknown type for subkey list (0x%04x): %c%c\n", nk->subkeys_offset, data.data[0], data.data[1]));
516 return WERR_GENERAL_FAILURE;
519 *ret = regf_get_key (ctx, key->hive->backend_data, key_off);
525 static WERROR regf_set_sec_desc (struct registry_key *key, struct security_descriptor *sec_desc)
528 return WERR_NOT_SUPPORTED;
531 static WERROR regf_get_sec_desc(TALLOC_CTX *ctx, struct registry_key *key, struct security_descriptor **sd)
533 struct nk_block *nk = key->backend_data;
535 struct regf_data *regf = key->hive->backend_data;
538 if (!hbin_get_tdr(regf, nk->sk_offset, ctx, (tdr_pull_fn_t) tdr_pull_sk_block, &sk)) {
539 DEBUG(0, ("Unable to find security descriptor\n"));
540 return WERR_GENERAL_FAILURE;
543 if (strcmp(sk.header, "sk") != 0) {
544 DEBUG(0, ("Expected 'sk', got '%s'\n", sk.header));
545 return WERR_GENERAL_FAILURE;
548 *sd = talloc(ctx, struct security_descriptor);
552 data.data = sk.sec_desc;
553 data.length = sk.rec_size;
554 if (NT_STATUS_IS_ERR(ndr_pull_struct_blob(&data, ctx, *sd, (ndr_pull_flags_fn_t)ndr_pull_security_descriptor))) {
555 DEBUG(0, ("Error parsing security descriptor\n"));
556 return WERR_GENERAL_FAILURE;
562 static uint32_t lf_add_entry (struct regf_data *regf, uint32_t list_offset, const char *name, uint32_t key_offset)
567 /* Add to subkeys list */
568 if (list_offset == -1) { /* Need to create subkeys list */
573 if (!hbin_get_tdr(regf, list_offset, regf, (tdr_pull_fn_t)tdr_pull_lf_block, &lf)) {
574 DEBUG(0, ("Can't get subkeys list\n"));
579 lf.hr = talloc_realloc(regf, lf.hr, struct hash_record, lf.key_count+1);
580 lf.hr[lf.key_count].nk_off = key_offset;
581 lf.hr[lf.key_count].hash = talloc_strndup(regf, name, 4);
584 ret = hbin_store_tdr_resize(regf, (tdr_push_fn_t)tdr_push_lf_block, list_offset, &lf);
591 static WERROR regf_del_value (struct registry_key *parent, const char *name)
594 return WERR_NOT_SUPPORTED;
598 static WERROR regf_del_key (struct registry_key *parent, const char *name)
600 struct nk_block *nk = parent->backend_data;
604 if (nk->subkeys_offset == -1)
609 return WERR_NOT_SUPPORTED;
612 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)
614 struct nk_block *parent_nk = parent->backend_data, nk;
615 struct regf_data *regf = parent->hive->backend_data;
619 nk.type = REG_SUB_KEY;
620 unix_to_nt_time(&nk.last_change, time(NULL));
622 nk.parent_offset = 0; /* FIXME */
625 nk.subkeys_offset = -1;
626 nk.unknown_offset = -1;
629 memset(nk.unk3, 0, 5);
630 nk.clsname_offset = -1;
631 nk.clsname_length = 0;
634 offset = hbin_store_tdr(regf, (tdr_push_fn_t) tdr_push_nk_block, &nk);
636 parent_nk->subkeys_offset = lf_add_entry(regf, parent_nk->subkeys_offset, name, nk.parent_offset);
638 parent_nk->num_subkeys++;
640 hbin_store_tdr_resize(regf, (tdr_push_fn_t) tdr_push_nk_block, nk.parent_offset, parent_nk);
642 *ret = regf_get_key(ctx, regf, offset);
644 /* FIXME: Set sec desc ! */
648 static WERROR regf_set_value (struct registry_key *key, const char *name, uint32_t type, DATA_BLOB data)
652 return WERR_NOT_SUPPORTED;
655 static WERROR regf_save_hbin(struct registry_hive *hive, struct hbin_block *hbin)
657 struct regf_data *regf = hive->backend_data;
659 /* go to right offset */
660 if (lseek(regf->fd, SEEK_SET, regf->header->data_offset + hbin->offset_from_first) == -1) {
661 DEBUG(0, ("Error lseeking in regf file\n"));
662 return WERR_GENERAL_FAILURE;
665 if (NT_STATUS_IS_ERR(tdr_push_to_fd(regf->fd, (tdr_push_fn_t)tdr_push_hbin_block, hbin))) {
666 DEBUG(0, ("Error writing HBIN block\n"));
667 return WERR_GENERAL_FAILURE;
673 static WERROR nt_open_hive (struct registry_hive *h, struct registry_key **key)
675 struct regf_data *regf;
676 struct regf_hdr *regf_hdr;
677 struct tdr_pull pull;
680 regf = (struct regf_data *)talloc_zero(h, struct regf_data);
681 h->backend_data = regf;
683 DEBUG(5, ("Attempting to load registry file\n"));
686 regf->fd = open(h->location, O_RDWR);
688 if (regf->fd == -1) {
689 DEBUG(0,("Could not load file: %s, %s\n", h->location,
691 return WERR_GENERAL_FAILURE;
695 pull.data.data = (uint8_t*)fd_load(regf->fd, &pull.data.length, regf);
697 if (pull.data.data == NULL) {
698 DEBUG(0, ("Error reading data\n"));
699 return WERR_GENERAL_FAILURE;
702 regf_hdr = talloc(regf, struct regf_hdr);
703 if (NT_STATUS_IS_ERR(tdr_pull_regf_hdr(&pull, regf_hdr, regf_hdr))) {
704 return WERR_GENERAL_FAILURE;
707 regf->header = regf_hdr;
709 if (strcmp(regf_hdr->REGF_ID, "regf") != 0) {
710 DEBUG(0, ("Unrecognized NT registry header id: %s, %s\n",
711 regf_hdr->REGF_ID, h->location));
714 DEBUG(1, ("Registry '%s' read. Version %d.%d.%d.%d\n",
715 regf_hdr->description, regf_hdr->version.major,
716 regf_hdr->version.minor, regf_hdr->version.release,
717 regf_hdr->version.build));
720 * Validate the header ...
722 if (regf_hdr_checksum(pull.data.data) != regf_hdr->chksum) {
723 DEBUG(0, ("Registry file checksum error: %s: %d,%d\n",
724 h->location, regf_hdr->chksum, regf_hdr_checksum(pull.data.data)));
725 return WERR_GENERAL_FAILURE;
728 pull.offset = 0x1000;
731 /* Read in all hbin blocks */
732 regf->hbins = talloc_array(regf, struct hbin_block *, 1);
733 regf->hbins[0] = NULL;
735 while (pull.offset < pull.data.length) {
736 struct hbin_block *hbin = talloc(regf->hbins, struct hbin_block);
738 if (NT_STATUS_IS_ERR(tdr_pull_hbin_block(&pull, hbin, hbin))) {
739 DEBUG(0, ("[%d] Error parsing HBIN block\n", i));
743 if (strcmp(hbin->HBIN_ID, "hbin") != 0) {
744 DEBUG(0, ("[%d] Expected 'hbin', got '%s'\n", i, hbin->HBIN_ID));
748 regf->hbins[i] = hbin;
750 regf->hbins = talloc_realloc(regf, regf->hbins, struct hbin_block *, i+2);
751 regf->hbins[i] = NULL;
754 DEBUG(1, ("%d HBIN blocks read\n", i));
756 *key = regf_get_key(h, regf, 0x20);
761 static struct hive_operations reg_backend_nt4 = {
763 .open_hive = nt_open_hive,
764 .num_subkeys = regf_num_subkeys,
765 .num_values = regf_num_values,
766 .get_subkey_by_index = regf_get_subkey,
767 .get_value_by_index = regf_get_value,
768 .key_get_sec_desc = regf_get_sec_desc,
769 .key_set_sec_desc = regf_set_sec_desc,
770 .add_key = regf_add_key,
771 .set_value = regf_set_value,
772 .del_key = regf_del_key,
773 .del_value = regf_del_value,
776 NTSTATUS registry_nt4_init(void)
778 return registry_register(®_backend_nt4);