r24665: Close file handles properly.
[sfrench/samba-autobuild/.git] / source4 / lib / registry / reg_backend_nt4.c
index 87ae2be070575bedbf9f65f0504d3c5d636bb40b..74261c57a99628fd7b5b21de0ef55c16222af2f1 100644 (file)
@@ -2,10 +2,11 @@
    Samba CIFS implementation
    Registry backend for REGF files
    Copyright (C) 2005 Jelmer Vernooij, jelmer@samba.org
+   Copyright (C) 2006 Wilco Baan Hofman, wilco@baanhofman.nl
 
    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 2 of the License, or
+   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,
    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, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
  
 #include "includes.h"
-#include "registry.h"
+#include "lib/registry/registry.h"
 #include "system/filesys.h"
 #include "system/time.h"
 #include "lib/registry/tdr_regf.h"
@@ -132,6 +132,8 @@ static DATA_BLOB hbin_alloc (struct regf_data *data, uint32_t size, uint32_t *of
        struct hbin_block *hbin = NULL;
        int i;
 
+       *offset = 0;
+
        if (size == 0)
                return data_blob(NULL, 0);
 
@@ -462,17 +464,17 @@ static WERROR regf_get_value (TALLOC_CTX *ctx, const struct registry_key *key, i
        }
 
        if ((*ret)->data.length < vk->data_length) {
-               DEBUG(1, ("Read data less then indicated data length!\n"));
+               DEBUG(1, ("Read data less than indicated data length!\n"));
        }
        
        return WERR_OK;
 }
 
-static WERROR regf_get_subkey (TALLOC_CTX *ctx, const struct registry_key *key, int idx, struct registry_key **ret)
+static WERROR regf_get_subkey_by_index (TALLOC_CTX *ctx, const struct registry_key *key, int idx, struct registry_key **ret)
 {
        DATA_BLOB data;
        struct nk_block *nk = key->backend_data;
-       uint32_t key_off;
+       uint32_t key_off=0;
 
        if (idx >= nk->num_subkeys)
                return WERR_NO_MORE_ITEMS;
@@ -484,8 +486,25 @@ static WERROR regf_get_subkey (TALLOC_CTX *ctx, const struct registry_key *key,
        }
 
        if (!strncmp((char *)data.data, "li", 2)) {
-               DEBUG(4, ("Subkeys in LI list\n"));
-               SMB_ASSERT(0);
+               struct li_block li;
+               struct tdr_pull pull;
+
+               DEBUG(10, ("Subkeys in LI list\n"));
+               ZERO_STRUCT(pull);
+               pull.data = data;
+
+               if (NT_STATUS_IS_ERR(tdr_pull_li_block(&pull, nk, &li))) {
+                       DEBUG(0, ("Error parsing LI list\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               SMB_ASSERT(!strncmp(li.header, "li",2));
+
+               if (li.key_count != nk->num_subkeys) {
+                       DEBUG(0, ("Subkey counts don't match\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               key_off = li.nk_offset[idx];
+       
        } else if (!strncmp((char *)data.data, "lf", 2)) {
                struct lf_block lf;
                struct tdr_pull pull;
@@ -498,19 +517,107 @@ static WERROR regf_get_subkey (TALLOC_CTX *ctx, const struct registry_key *key,
                        DEBUG(0, ("Error parsing LF list\n"));
                        return WERR_GENERAL_FAILURE;
                }
+               SMB_ASSERT(!strncmp(lf.header, "lf",2));
 
                if (lf.key_count != nk->num_subkeys) {
                        DEBUG(0, ("Subkey counts don't match\n"));
                        return WERR_GENERAL_FAILURE;
                }
 
-               key_off = lf.hr[idx].nk_off;
-       } else if (!strncmp((char *)data.data, "ri", 2)) {
-               DEBUG(4, ("Subkeys in RI list\n"));
-               SMB_ASSERT(0);
+               key_off = lf.hr[idx].nk_offset;
        } else if (!strncmp((char *)data.data, "lh", 2)) {
-               DEBUG(4, ("Subkeys in LH list\n"));
-               SMB_ASSERT(0);
+               struct lh_block lh;
+               struct tdr_pull pull;
+               
+               DEBUG(10, ("Subkeys in LH list"));
+               ZERO_STRUCT(pull);
+               pull.data = data;
+               
+               if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, nk, &lh))) {
+                       DEBUG(0, ("Error parsing LH list\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               SMB_ASSERT(!strncmp(lh.header, "lh",2));
+               
+               if (lh.key_count != nk->num_subkeys) {
+                       DEBUG(0, ("Subkey counts don't match\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               key_off = lh.hr[idx].nk_offset;
+       } else if (!strncmp((char *)data.data, "ri", 2)) {
+               struct ri_block ri;
+               struct tdr_pull pull;
+               uint16_t i;
+               uint16_t sublist_count = 0;
+               
+               ZERO_STRUCT(pull);
+               pull.data = data;
+               
+               if (NT_STATUS_IS_ERR(tdr_pull_ri_block(&pull, nk, &ri))) {
+                       DEBUG(0, ("Error parsing RI list\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               SMB_ASSERT(!strncmp(ri.header, "ri",2));
+               
+               for (i = 0; i < ri.key_count; i++) {
+                       DATA_BLOB list_data;
+                       
+                       /* Get sublist data blob */
+                       list_data = hbin_get(key->hive->backend_data, ri.offset[i]);
+                       if (!list_data.data) {
+                               DEBUG(0, ("Error getting RI list."));
+                               return WERR_GENERAL_FAILURE;
+                       }
+                       
+                       ZERO_STRUCT(pull);
+                       pull.data = list_data;
+                       
+                       if (!strncmp((char *)list_data.data, "li", 2)) {
+                               struct li_block li;
+
+                               if (NT_STATUS_IS_ERR(tdr_pull_li_block(&pull, nk, &li))) {
+                                       DEBUG(0, ("Error parsing LI list from RI\n"));
+                                       return WERR_GENERAL_FAILURE;
+                               }
+                               SMB_ASSERT(!strncmp(li.header, "li",2));
+                               
+                               /* Advance to next sublist if necessary */
+                               if (idx >= sublist_count + li.key_count) {
+                                       sublist_count += li.key_count;
+                                       continue;
+                               }
+                               key_off = li.nk_offset[idx - sublist_count];
+                               sublist_count += li.key_count;
+                               break;
+                       } else if (!strncmp((char *)list_data.data, "lh", 2)) {
+                               struct lh_block lh;
+                               
+                               if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, nk, &lh))) {
+                                       DEBUG(0, ("Error parsing LH list from RI\n"));
+                                       return WERR_GENERAL_FAILURE;
+                               }
+                               SMB_ASSERT(!strncmp(lh.header, "lh",2));
+
+                               
+                               /* Advance to next sublist if necessary */
+                               if (idx >= sublist_count + lh.key_count) {
+                                       sublist_count += lh.key_count;
+                                       continue;
+                               }
+                               key_off = lh.hr[idx - sublist_count].nk_offset;
+                               sublist_count += lh.key_count;
+                               break;
+                       } else {
+                               DEBUG(0,("Unknown sublist in ri block\n"));
+                               SMB_ASSERT(0);
+                       }
+                       
+               }
+       
+               if (idx > sublist_count) {
+                       return WERR_NO_MORE_ITEMS;
+               }
+
        } else {
                DEBUG(0, ("Unknown type for subkey list (0x%04x): %c%c\n", nk->subkeys_offset, data.data[0], data.data[1]));
                return WERR_GENERAL_FAILURE;
@@ -521,6 +628,239 @@ static WERROR regf_get_subkey (TALLOC_CTX *ctx, const struct registry_key *key,
        return WERR_OK;
 }
 
+static WERROR regf_match_subkey_by_name (TALLOC_CTX *ctx, const struct registry_key *key, uint32_t offset, const char *name, uint32_t *ret) 
+{
+       DATA_BLOB subkey_data;
+       struct nk_block subkey;
+       struct tdr_pull pull;
+       
+       subkey_data = hbin_get(key->hive->backend_data, offset);
+       if (!subkey_data.data) {
+               DEBUG(0, ("Unable to retrieve subkey HBIN\n"));
+               return WERR_GENERAL_FAILURE;
+       }
+
+       ZERO_STRUCT(pull);
+       pull.data = subkey_data;
+       
+       if (NT_STATUS_IS_ERR(tdr_pull_nk_block(&pull, ctx, &subkey))) {
+               DEBUG(0, ("Error parsing NK structure.\n"));
+               return WERR_GENERAL_FAILURE;
+       }
+       if (strncmp(subkey.header, "nk", 2)) {
+               DEBUG(0, ("Not an NK structure.\n"));
+               return WERR_GENERAL_FAILURE;
+       }
+       if (!strcasecmp(subkey.key_name, name)) {
+               *ret = offset;
+       } else {
+               *ret = 0;
+       }
+       return WERR_OK;
+}
+       
+static WERROR regf_get_subkey_by_name (TALLOC_CTX *ctx, const struct registry_key *key, const char *name, struct registry_key **ret)
+{
+       DATA_BLOB data;
+       struct nk_block *nk = key->backend_data;
+       uint32_t key_off = 0;
+
+       data = hbin_get(key->hive->backend_data, nk->subkeys_offset);
+       if (!data.data) {
+               DEBUG(0, ("Unable to find subkey list\n"));
+               return WERR_GENERAL_FAILURE;
+       }
+
+       if (!strncmp((char *)data.data, "li",2)) {
+               struct li_block li;
+               struct tdr_pull pull;
+               uint16_t i;
+
+               DEBUG(10, ("Subkeys in LI list\n"));
+               ZERO_STRUCT(pull);
+               pull.data = data;
+               
+               if (NT_STATUS_IS_ERR(tdr_pull_li_block(&pull, nk, &li))) {
+                       DEBUG(0, ("Error parsing LI list\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               SMB_ASSERT(!strncmp(li.header, "li",2));
+
+               if (li.key_count != nk->num_subkeys) {
+                       DEBUG(0, ("Subkey counts don't match\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               
+               for (i = 0; i < li.key_count; i++) {
+                       W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key, li.nk_offset[i], name, &key_off));
+                       if (key_off) {
+                               break;
+                       }
+               }
+               if (!key_off) {
+                       return WERR_DEST_NOT_FOUND;
+               }
+       } else if (!strncmp((char *)data.data, "lf",2)) {
+               struct lf_block lf;
+               struct tdr_pull pull;
+               uint16_t i;
+
+               DEBUG(10, ("Subkeys in LF list\n"));
+               ZERO_STRUCT(pull);
+               pull.data = data;
+               
+               if (NT_STATUS_IS_ERR(tdr_pull_lf_block(&pull, nk, &lf))) {
+                       DEBUG(0, ("Error parsing LF list\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               SMB_ASSERT(!strncmp(lf.header, "lf",2));
+
+               if (lf.key_count != nk->num_subkeys) {
+                       DEBUG(0, ("Subkey counts don't match\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               
+               for (i = 0; i < lf.key_count; i++) {
+                       if (strncmp(lf.hr[i].hash, name, 4)) {
+                               continue;
+                       }
+                       W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key, lf.hr[i].nk_offset, name, &key_off));
+                       if (key_off) {
+                               break;
+                       }
+               }
+               if (!key_off) {
+                       return WERR_DEST_NOT_FOUND;
+               }
+       } else if (!strncmp((char *)data.data, "lh",2)) {
+               struct lh_block lh;
+               struct tdr_pull pull;
+               uint16_t i;
+               uint32_t hash = 0;
+               char *hash_name;
+
+               DEBUG(10, ("Subkeys in LH list\n"));
+               ZERO_STRUCT(pull);
+               pull.data = data;
+               
+               if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, nk, &lh))) {
+                       DEBUG(0, ("Error parsing LH list\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               SMB_ASSERT(!strncmp(lh.header, "lh",2));
+
+               if (lh.key_count != nk->num_subkeys) {
+                       DEBUG(0, ("Subkey counts don't match\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               
+               /* Compute hash for the name */
+               hash_name = strupper_talloc(nk, name);          
+               for (i = 0; *(hash_name + i) != 0; i++) {
+                       hash *= 37;
+                       hash += *(hash_name + i);
+               }
+               for (i = 0; i < lh.key_count; i++) {
+                       if (lh.hr[i].base37 != hash) {
+                               continue;
+                       }
+                       W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key, lh.hr[i].nk_offset, name, &key_off));
+                       if (key_off) {
+                               break;
+                       }
+               }       
+               if (!key_off) {
+                       return WERR_DEST_NOT_FOUND;
+               }
+       } else if (!strncmp((char *)data.data, "ri", 2)) {
+               struct ri_block ri;
+               struct tdr_pull pull;
+               uint16_t i, j;
+
+               DEBUG(10, ("Subkeys in RI list\n"));
+               ZERO_STRUCT(pull);
+               pull.data = data;
+               
+               if (NT_STATUS_IS_ERR(tdr_pull_ri_block(&pull, nk, &ri))) {
+                       DEBUG(0, ("Error parsing RI list\n"));
+                       return WERR_GENERAL_FAILURE;
+               }
+               SMB_ASSERT(!strncmp(ri.header, "ri",2));
+
+                       
+               for (i = 0; i < ri.key_count; i++) {
+                       DATA_BLOB list_data;
+                       
+                       /* Get sublist data blob */
+                       list_data = hbin_get(key->hive->backend_data, ri.offset[i]);
+                       if (!list_data.data) {
+                               DEBUG(0, ("Error getting RI list."));
+                               return WERR_GENERAL_FAILURE;
+                       }
+                               
+                       ZERO_STRUCT(pull);
+                       pull.data = list_data;
+                       
+                       if (!strncmp((char *)list_data.data, "li", 2)) {
+                               struct li_block li;
+       
+                               if (NT_STATUS_IS_ERR(tdr_pull_li_block(&pull, nk, &li))) {
+                                       DEBUG(0, ("Error parsing LI list from RI\n"));
+                                       return WERR_GENERAL_FAILURE;
+                               }
+                               SMB_ASSERT(!strncmp(li.header, "li",2));
+                               
+                               for (j = 0; j < li.key_count; j++) {
+                                       W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key, 
+                                                               li.nk_offset[j], name, &key_off));
+                                       if (key_off) {
+                                               break;
+                                       }
+                               }
+                       } else if (!strncmp((char *)list_data.data, "lh", 2)) {
+                               struct lh_block lh;
+                               uint32_t hash = 0;
+                               char *hash_name;
+                               
+                               if (NT_STATUS_IS_ERR(tdr_pull_lh_block(&pull, nk, &lh))) {
+                                       DEBUG(0, ("Error parsing LH list from RI\n"));
+                                       return WERR_GENERAL_FAILURE;
+                               }
+                               SMB_ASSERT(!strncmp(lh.header, "lh",2));
+
+                               /* Compute hash for the name */
+                               hash_name = strupper_talloc(nk, name);          
+                               for (j = 0; *(hash_name + j) != 0; j++) {
+                                       hash *= 37;
+                                       hash += *(hash_name + j);
+                               }
+                               for (j = 0; j < lh.key_count; j++) {
+                                       if (lh.hr[j].base37 != hash) {
+                                               continue;
+                                       }
+                                       W_ERROR_NOT_OK_RETURN(regf_match_subkey_by_name(nk, key, 
+                                                               lh.hr[j].nk_offset, name, &key_off));
+                                       if (key_off) {
+                                               break;
+                                       }
+                               }
+                       }
+                       if (key_off) {
+                               break;
+                       }
+                               
+               }
+               if (!key_off) {
+                       return WERR_DEST_NOT_FOUND;
+               }
+       } else {
+               DEBUG(0, ("Unknown subkey list type.\n"));
+               return WERR_GENERAL_FAILURE;
+       }
+
+       *ret = regf_get_key (ctx, key->hive->backend_data, key_off);
+       return WERR_OK;
+}
 
 static WERROR regf_set_sec_desc (const struct registry_key *key, const struct security_descriptor *sec_desc)
 {
@@ -564,11 +904,11 @@ static uint32_t lf_add_entry (struct regf_data *regf, uint32_t list_offset, cons
        uint32_t ret;
        struct lf_block lf;
 
+       ZERO_STRUCT(lf);
+
        /* Add to subkeys list */
        if (list_offset == -1) { /* Need to create subkeys list */
                lf.header = "lf";
-               lf.key_count = 0;
-               lf.hr = NULL;
        } else {
                if (!hbin_get_tdr(regf, list_offset, regf, (tdr_pull_fn_t)tdr_pull_lf_block, &lf)) {
                        DEBUG(0, ("Can't get subkeys list\n"));
@@ -577,7 +917,7 @@ static uint32_t lf_add_entry (struct regf_data *regf, uint32_t list_offset, cons
        }
 
        lf.hr = talloc_realloc(regf, lf.hr, struct hash_record, lf.key_count+1);
-       lf.hr[lf.key_count].nk_off = key_offset;
+       lf.hr[lf.key_count].nk_offset = key_offset;
        lf.hr[lf.key_count].hash = talloc_strndup(regf, name, 4);
        lf.key_count++;
 
@@ -736,7 +1076,7 @@ static WERROR nt_open_hive (struct registry_hive *h, struct registry_key **key)
        regf->hbins = talloc_array(regf, struct hbin_block *, 1);
        regf->hbins[0] = NULL;
 
-       while (pull.offset < pull.data.length) {
+       while (pull.offset < pull.data.length && pull.offset < regf->header->last_block) {
                struct hbin_block *hbin = talloc(regf->hbins, struct hbin_block);
 
                if (NT_STATUS_IS_ERR(tdr_pull_hbin_block(&pull, hbin, hbin))) {
@@ -767,7 +1107,8 @@ static struct hive_operations reg_backend_nt4 = {
        .open_hive = nt_open_hive,
        .num_subkeys = regf_num_subkeys,
        .num_values = regf_num_values,
-       .get_subkey_by_index = regf_get_subkey,
+       .get_subkey_by_index = regf_get_subkey_by_index,
+       .get_subkey_by_name = regf_get_subkey_by_name,
        .get_value_by_index = regf_get_value,
        .key_get_sec_desc = regf_get_sec_desc,
        .key_set_sec_desc = regf_set_sec_desc,