registry: Fix CID 241075 Unchecked return value
[kai/samba-autobuild/.git] / source4 / lib / registry / ldb.c
index b21ca8e27be796437c642ccda023f17b92fcb727..1dfffdb7acc5f98f68f90483fc51717ffac5fa1c 100644 (file)
@@ -20,8 +20,8 @@
 
 #include "includes.h"
 #include "registry.h"
-#include "lib/ldb/include/ldb.h"
-#include "lib/ldb/include/ldb_errors.h"
+#include <ldb.h>
+#include <ldb_errors.h>
 #include "ldb_wrap.h"
 #include "librpc/gen_ndr/winreg.h"
 #include "param/param.h"
@@ -35,6 +35,7 @@ struct ldb_key_data
        struct ldb_dn *dn;
        struct ldb_message **subkeys, **values;
        unsigned int subkey_count, value_count;
+       const char *classname;
 };
 
 static void reg_ldb_unpack_value(TALLOC_CTX *mem_ctx,
@@ -48,7 +49,7 @@ static void reg_ldb_unpack_value(TALLOC_CTX *mem_ctx,
        if (name != NULL) {
                *name = talloc_strdup(mem_ctx,
                                      ldb_msg_find_attr_as_string(msg, "value",
-                                     NULL));
+                                     ""));
        }
 
        value_type = ldb_msg_find_attr_as_uint(msg, "type", 0);
@@ -61,22 +62,10 @@ static void reg_ldb_unpack_value(TALLOC_CTX *mem_ctx,
        case REG_SZ:
        case REG_EXPAND_SZ:
                if (val != NULL) {
-                       if (val->data[0] != '\0') {
-                               /* The data should be provided as UTF16 string */
-                               convert_string_talloc(mem_ctx, CH_UTF8, CH_UTF16,
-                                                     val->data, val->length,
-                                                     (void **)&data->data, &data->length, false);
-                       } else {
-                               /* Provide a possibility to store also UTF8
-                                * REG_SZ/REG_EXPAND_SZ values. This is done
-                                * by adding a '\0' in front of the data */
-                               data->data = talloc_size(mem_ctx, val->length - 1);
-                               if (data->data != NULL) {
-                                       memcpy(data->data, val->data + 1,
-                                              val->length - 1);
-                               }
-                               data->length = val->length - 1;
-                       }
+                       /* The data should be provided as UTF16 string */
+                       convert_string_talloc(mem_ctx, CH_UTF8, CH_UTF16,
+                                             val->data, val->length,
+                                             (void **)&data->data, &data->length);
                } else {
                        data->data = NULL;
                        data->length = 0;
@@ -86,25 +75,13 @@ static void reg_ldb_unpack_value(TALLOC_CTX *mem_ctx,
        case REG_DWORD:
        case REG_DWORD_BIG_ENDIAN:
                if (val != NULL) {
-                       if (val->data[0] != '\0') {
-                               /* The data is a plain DWORD */
-                               uint32_t tmp = strtoul((char *)val->data, NULL, 0);
-                               data->data = talloc_size(mem_ctx, sizeof(uint32_t));
-                               if (data->data != NULL) {
-                                       SIVAL(data->data, 0, tmp);
-                               }
-                               data->length = sizeof(uint32_t);
-                       } else {
-                               /* Provide a possibility to store also UTF8
-                                * REG_DWORD values. This is done by adding a
-                                * '\0' in front of the data */
-                               data->data = talloc_size(mem_ctx, val->length - 1);
-                               if (data->data != NULL) {
-                                       memcpy(data->data, val->data + 1,
-                                              val->length - 1);
-                               }
-                               data->length = val->length - 1;
+                       /* The data is a plain DWORD */
+                       uint32_t tmp = strtoul((char *)val->data, NULL, 0);
+                       data->data = talloc_size(mem_ctx, sizeof(uint32_t));
+                       if (data->data != NULL) {
+                               SIVAL(data->data, 0, tmp);
                        }
+                       data->length = sizeof(uint32_t);
                } else {
                        data->data = NULL;
                        data->length = 0;
@@ -113,25 +90,13 @@ static void reg_ldb_unpack_value(TALLOC_CTX *mem_ctx,
 
        case REG_QWORD:
                if (val != NULL) {
-                       if (val->data[0] != '\0') {
-                               /* The data is a plain QWORD */
-                               uint64_t tmp = strtoull((char *)val->data, NULL, 0);
-                               data->data = talloc_size(mem_ctx, sizeof(uint64_t));
-                               if (data->data != NULL) {
-                                       SBVAL(data->data, 0, tmp);
-                               }
-                               data->length = sizeof(uint64_t);
-                       } else {
-                               /* Provide a possibility to store also UTF8
-                                * REG_QWORD values. This is done by adding a
-                                * '\0' in front of the data */
-                               data->data = talloc_size(mem_ctx, val->length - 1);
-                               if (data->data != NULL) {
-                                       memcpy(data->data, val->data + 1,
-                                              val->length - 1);
-                               }
-                               data->length = val->length - 1;
+                       /* The data is a plain QWORD */
+                       uint64_t tmp = strtoull((char *)val->data, NULL, 0);
+                       data->data = talloc_size(mem_ctx, sizeof(uint64_t));
+                       if (data->data != NULL) {
+                               SBVAL(data->data, 0, tmp);
                        }
+                       data->length = sizeof(uint64_t);
                } else {
                        data->data = NULL;
                        data->length = 0;
@@ -191,32 +156,16 @@ static struct ldb_message *reg_ldb_pack_value(struct ldb_context *ctx,
                                return NULL;
                        }
 
-                       /* Only when the "data.length" is dividable by two try
-                        * the charset conversion, otherwise stick with the
-                        * default of the "ret2" variable set to "false" (which
-                        * means binary storage and no conversion) */
-                       if (data.length % 2 == 0) {
-                               /* The data is provided as UTF16 string */
-                               ret2 = convert_string_talloc(mem_ctx, CH_UTF16, CH_UTF8,
-                                                            (void *)data.data, data.length,
-                                                            (void **)&val->data, &val->length,
-                                                            false);
-                       }
-                       if (!ret2) {
-                               /* Provide a possibility to store also binary
-                                * UTF8 REG_SZ/REG_EXPAND_SZ values as fallback
-                                * mechanism. This is done by adding a '\0' in
-                                * front of the data */
-                               val->data = talloc_size(msg, data.length + 1);
-                               if (val->data == NULL) {
-                                       talloc_free(msg);
-                                       return NULL;
-                               }
-                               val->data[0] = '\0';
-                               memcpy(val->data + 1, data.data, data.length);
-                               val->length = data.length + 1;
+                       /* The data is provided as UTF16 string */
+                       ret2 = convert_string_talloc(mem_ctx, CH_UTF16, CH_UTF8,
+                                                    (void *)data.data, data.length,
+                                                    (void **)&val->data, &val->length);
+                       if (ret2) {
+                               ret = ldb_msg_add_value(msg, "data", val, NULL);
+                       } else {
+                               /* workaround for non-standard data */
+                               ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
                        }
-                       ret = ldb_msg_add_value(msg, "data", val, NULL);
                } else {
                        ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
                }
@@ -236,26 +185,9 @@ static struct ldb_message *reg_ldb_pack_value(struct ldb_context *ctx,
                                }
                                ret = ldb_msg_add_string(msg, "data", conv_str);
                        } else {
-                               /* Provide a possibility to store also UTF8
-                                * REG_DWORD values. This is done by adding a
-                                * '\0' in front of the data */
-                               struct ldb_val *val;
-
-                               val = talloc_zero(msg, struct ldb_val);
-                               if (val == NULL) {
-                                       talloc_free(msg);
-                                       return NULL;
-                               }
-
-                               val->data = talloc_size(msg, data.length + 1);
-                               if (val->data == NULL) {
-                                       talloc_free(msg);
-                                       return NULL;
-                               }
-                               val->data[0] = '\0';
-                               memcpy(val->data + 1, data.data, data.length);
-                               val->length = data.length + 1;
-                               ret = ldb_msg_add_value(msg, "data", val, NULL);
+                               /* workaround for non-standard data */
+                               talloc_free(msg);
+                               return NULL;
                        }
                } else {
                        ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
@@ -267,33 +199,18 @@ static struct ldb_message *reg_ldb_pack_value(struct ldb_context *ctx,
                        if (data.length == sizeof(uint64_t)) {
                                char *conv_str;
 
-                               conv_str = talloc_asprintf(msg, "0x%16.16llx", BVAL(data.data, 0));
+                               conv_str = talloc_asprintf(msg, "0x%16.16llx",
+                                                          (unsigned long long)BVAL(data.data, 0));
                                if (conv_str == NULL) {
                                        talloc_free(msg);
                                        return NULL;
                                }
                                ret = ldb_msg_add_string(msg, "data", conv_str);
                        } else {
-                               /* Provide a possibility to store also UTF8
-                                * REG_QWORD values. This is done by adding a
-                                * '\0' in front of the data */
-                               struct ldb_val *val;
-
-                               val = talloc_zero(msg, struct ldb_val);
-                               if (val == NULL) {
-                                       talloc_free(msg);
-                                       return NULL;
-                               }
+                               /* workaround for non-standard data */
+                               talloc_free(msg);
+                               return NULL;
 
-                               val->data = talloc_size(msg, data.length + 1);
-                               if (val->data == NULL) {
-                                       talloc_free(msg);
-                                       return NULL;
-                               }
-                               val->data[0] = '\0';
-                               memcpy(val->data + 1, data.data, data.length);
-                               val->length = data.length + 1;
-                               ret = ldb_msg_add_value(msg, "data", val, NULL);
                        }
                } else {
                        ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
@@ -302,8 +219,7 @@ static struct ldb_message *reg_ldb_pack_value(struct ldb_context *ctx,
 
        case REG_BINARY:
        default:
-               if ((data.length > 0) && (data.data != NULL)
-                   && (data.data[0] != '\0')) {
+               if ((data.length > 0) && (data.data != NULL)) {
                        ret = ldb_msg_add_value(msg, "data", &data, NULL);
                } else {
                        ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
@@ -359,51 +275,47 @@ static struct ldb_dn *reg_path_to_ldb(TALLOC_CTX *mem_ctx,
                                      const struct hive_key *from,
                                      const char *path, const char *add)
 {
-       TALLOC_CTX *local_ctx;
        struct ldb_dn *ret;
-       char *mypath = talloc_strdup(mem_ctx, path);
+       char *mypath;
        char *begin;
        struct ldb_key_data *kd = talloc_get_type(from, struct ldb_key_data);
        struct ldb_context *ldb = kd->ldb;
 
-       local_ctx = talloc_new(mem_ctx);
+       mypath = talloc_strdup(mem_ctx, path);
+       if (mypath == NULL) {
+               return NULL;
+       }
 
        ret = ldb_dn_new(mem_ctx, ldb, add);
        if (!ldb_dn_validate(ret)) {
                talloc_free(ret);
-               talloc_free(local_ctx);
                return NULL;
        }
 
-       while (mypath) {
-               char *keyname;
-
-               begin = strrchr(mypath, '\\');
+       if (!ldb_dn_add_base(ret, kd->dn)) {
+               talloc_free(ret);
+               return NULL;
+       }
 
-               if (begin) keyname = begin + 1;
-               else keyname = mypath;
+       while (mypath[0] != '\0') {
+               begin = strchr(mypath, '\\');
+               if (begin != NULL) {
+                       *begin = '\0';
+               }
 
-               if (keyname[0] != '\0') {
-                       if (!ldb_dn_add_base_fmt(ret, "key=%s",
-                                                reg_ldb_escape(local_ctx,
-                                                               keyname)))
-                       {
-                               talloc_free(local_ctx);
-                               return NULL;
-                       }
+               if (!ldb_dn_add_child_fmt(ret, "key=%s",
+                                         reg_ldb_escape(mem_ctx, mypath))) {
+                       talloc_free(ret);
+                       return NULL;
                }
 
-               if(begin) {
-                       *begin = '\0';
+               if (begin != NULL) {
+                       mypath = begin + 1;
                } else {
                        break;
                }
        }
 
-       ldb_dn_add_base(ret, kd->dn);
-
-       talloc_free(local_ctx);
-
        return ret;
 }
 
@@ -413,8 +325,8 @@ static WERROR cache_subkeys(struct ldb_key_data *kd)
        struct ldb_result *res;
        int ret;
 
-       ret = ldb_search(c, c, &res, kd->dn, LDB_SCOPE_ONELEVEL, NULL, "(key=*)");
-
+       ret = ldb_search(c, c, &res, kd->dn, LDB_SCOPE_ONELEVEL,
+                        NULL, "(key=*)");
        if (ret != LDB_SUCCESS) {
                DEBUG(0, ("Error getting subkeys for '%s': %s\n",
                        ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
@@ -436,7 +348,6 @@ static WERROR cache_values(struct ldb_key_data *kd)
 
        ret = ldb_search(c, c, &res, kd->dn, LDB_SCOPE_ONELEVEL,
                         NULL, "(value=*)");
-
        if (ret != LDB_SUCCESS) {
                DEBUG(0, ("Error getting values for '%s': %s\n",
                        ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
@@ -457,14 +368,13 @@ static WERROR ldb_get_subkey_by_id(TALLOC_CTX *mem_ctx,
                                   const char **classname,
                                   NTTIME *last_mod_time)
 {
-       struct ldb_message_element *el;
        struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
 
        /* Initialization */
        if (name != NULL)
                *name = NULL;
        if (classname != NULL)
-               *classname = NULL; /* TODO: Store properly */
+               *classname = NULL;
        if (last_mod_time != NULL)
                *last_mod_time = 0; /* TODO: we need to add this to the
                                                ldb backend properly */
@@ -477,19 +387,20 @@ static WERROR ldb_get_subkey_by_id(TALLOC_CTX *mem_ctx,
        if (idx >= kd->subkey_count)
                return WERR_NO_MORE_ITEMS;
 
-       el = ldb_msg_find_element(kd->subkeys[idx], "key");
-       SMB_ASSERT(el != NULL);
-       SMB_ASSERT(el->num_values != 0);
-
        if (name != NULL)
-               *name = talloc_strdup(mem_ctx, (char *)el->values[0].data);
+               *name = talloc_strdup(mem_ctx,
+                                     ldb_msg_find_attr_as_string(kd->subkeys[idx], "key", NULL));
+       if (classname != NULL)
+               *classname = talloc_strdup(mem_ctx,
+                                          ldb_msg_find_attr_as_string(kd->subkeys[idx], "classname", NULL));
 
        return WERR_OK;
 }
 
-static WERROR ldb_get_default_value(TALLOC_CTX *mem_ctx, struct hive_key *k,
-                                 const char **name, uint32_t *data_type,
-                                  DATA_BLOB *data)
+static WERROR ldb_get_default_value(TALLOC_CTX *mem_ctx,
+                                   const struct hive_key *k,
+                                   const char **name, uint32_t *data_type,
+                                   DATA_BLOB *data)
 {
        struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
        struct ldb_context *c = kd->ldb;
@@ -497,7 +408,8 @@ static WERROR ldb_get_default_value(TALLOC_CTX *mem_ctx, struct hive_key *k,
        struct ldb_result *res;
        int ret;
 
-       ret = ldb_search(c, mem_ctx, &res, kd->dn, LDB_SCOPE_BASE, attrs, "(key=*)");
+       ret = ldb_search(c, mem_ctx, &res, kd->dn, LDB_SCOPE_BASE, attrs,
+                        NULL);
 
        if (ret != LDB_SUCCESS) {
                DEBUG(0, ("Error getting default value for '%s': %s\n",
@@ -505,11 +417,15 @@ static WERROR ldb_get_default_value(TALLOC_CTX *mem_ctx, struct hive_key *k,
                return WERR_FOOBAR;
        }
 
-       if (res->count == 0 || res->msgs[0]->num_elements == 0)
+       if (res->count == 0 || res->msgs[0]->num_elements == 0) {
+               talloc_free(res);
                return WERR_BADFILE;
+       }
 
-       reg_ldb_unpack_value(mem_ctx,
-                res->msgs[0], name, data_type, data);
+       if ((data_type != NULL) && (data != NULL)) {
+               reg_ldb_unpack_value(mem_ctx, res->msgs[0], name, data_type,
+                                    data);
+       }
 
        talloc_free(res);
 
@@ -522,7 +438,7 @@ static WERROR ldb_get_value_by_id(TALLOC_CTX *mem_ctx, struct hive_key *k,
 {
        struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
 
-       /* if default value exists, give it back */
+       /* if the default value exists, give it back */
        if (W_ERROR_IS_OK(ldb_get_default_value(mem_ctx, k, name, data_type,
                data))) {
                if (idx == 0)
@@ -549,69 +465,68 @@ static WERROR ldb_get_value(TALLOC_CTX *mem_ctx, struct hive_key *k,
                            DATA_BLOB *data)
 {
        struct ldb_key_data *kd = talloc_get_type(k, struct ldb_key_data);
-       struct ldb_context *c = kd->ldb;
-       struct ldb_result *res;
-       int ret;
-
-       if (name == NULL) {
-               return WERR_INVALID_PARAM;
-       }
+       const char *res_name;
+       uint32_t idx;
 
+       /* the default value was requested, give it back */
        if (name[0] == '\0') {
-               /* default value */
                return ldb_get_default_value(mem_ctx, k, NULL, data_type, data);
-       } else {
-               /* normal value */
-               ret = ldb_search(c, mem_ctx, &res, kd->dn, LDB_SCOPE_ONELEVEL,
-                                NULL, "(value=%s)", name);
-
-               if (ret != LDB_SUCCESS) {
-                       DEBUG(0, ("Error getting values for '%s': %s\n",
-                               ldb_dn_get_linearized(kd->dn), ldb_errstring(c)));
-                       return WERR_FOOBAR;
-               }
-
-               if (res->count == 0)
-                       return WERR_BADFILE;
+       }
 
-               reg_ldb_unpack_value(mem_ctx, res->msgs[0], NULL, data_type, data);
+       /* Do the search if necessary */
+       if (kd->values == NULL) {
+               W_ERROR_NOT_OK_RETURN(cache_values(kd));
+       }
 
-               talloc_free(res);
+       for (idx = 0; idx < kd->value_count; idx++) {
+               res_name = ldb_msg_find_attr_as_string(kd->values[idx], "value",
+                                                      "");
+               if (ldb_attr_cmp(name, res_name) == 0) {
+                       reg_ldb_unpack_value(mem_ctx, kd->values[idx], NULL,
+                                            data_type, data);
+                       return WERR_OK;
+               }
        }
 
-       return WERR_OK;
+       return WERR_BADFILE;
 }
 
 static WERROR ldb_open_key(TALLOC_CTX *mem_ctx, const struct hive_key *h,
                           const char *name, struct hive_key **key)
 {
        struct ldb_result *res;
-       struct ldb_dn *ldap_path;
+       struct ldb_dn *ldb_path;
        int ret;
        struct ldb_key_data *newkd;
        struct ldb_key_data *kd = talloc_get_type(h, struct ldb_key_data);
        struct ldb_context *c = kd->ldb;
 
-       ldap_path = reg_path_to_ldb(mem_ctx, h, name, NULL);
-       W_ERROR_HAVE_NO_MEMORY(ldap_path);
+       ldb_path = reg_path_to_ldb(mem_ctx, h, name, NULL);
+       W_ERROR_HAVE_NO_MEMORY(ldb_path);
 
-       ret = ldb_search(c, mem_ctx, &res, ldap_path, LDB_SCOPE_BASE, NULL, "(key=*)");
+       ret = ldb_search(c, mem_ctx, &res, ldb_path, LDB_SCOPE_BASE, NULL,
+                        NULL);
 
        if (ret != LDB_SUCCESS) {
                DEBUG(3, ("Error opening key '%s': %s\n",
-                       ldb_dn_get_linearized(ldap_path), ldb_errstring(c)));
+                       ldb_dn_get_linearized(ldb_path), ldb_errstring(c)));
                return WERR_FOOBAR;
        } else if (res->count == 0) {
                DEBUG(3, ("Key '%s' not found\n",
-                       ldb_dn_get_linearized(ldap_path)));
+                       ldb_dn_get_linearized(ldb_path)));
                talloc_free(res);
                return WERR_BADFILE;
        }
 
        newkd = talloc_zero(mem_ctx, struct ldb_key_data);
+       W_ERROR_HAVE_NO_MEMORY(newkd);
        newkd->key.ops = &reg_backend_ldb;
        newkd->ldb = talloc_reference(newkd, kd->ldb);
-       newkd->dn = ldb_dn_copy(mem_ctx, res->msgs[0]->dn);
+       newkd->dn = ldb_dn_copy(newkd, res->msgs[0]->dn);
+       newkd->classname = talloc_steal(newkd,
+                                       ldb_msg_find_attr_as_string(res->msgs[0], "classname", NULL));
+
+       talloc_free(res);
 
        *key = (struct hive_key *)newkd;
 
@@ -668,22 +583,28 @@ static WERROR ldb_add_key(TALLOC_CTX *mem_ctx, const struct hive_key *parent,
                          struct hive_key **newkey)
 {
        struct ldb_key_data *parentkd = discard_const_p(struct ldb_key_data, parent);
+       struct ldb_dn *ldb_path;
        struct ldb_message *msg;
        struct ldb_key_data *newkd;
        int ret;
 
+       ldb_path = reg_path_to_ldb(mem_ctx, parent, name, NULL);
+       W_ERROR_HAVE_NO_MEMORY(ldb_path);
+
        msg = ldb_msg_new(mem_ctx);
        W_ERROR_HAVE_NO_MEMORY(msg);
 
-       msg->dn = reg_path_to_ldb(msg, parent, name, NULL);
-       W_ERROR_HAVE_NO_MEMORY(msg->dn);
+       msg->dn = ldb_path;
 
-       ldb_msg_add_string(msg, "key", talloc_strdup(mem_ctx, name));
-       if (classname != NULL)
-               ldb_msg_add_string(msg, "classname",
-                                  talloc_strdup(mem_ctx, classname));
+       ldb_msg_add_string(msg, "key", name);
+       if (classname != NULL) {
+               ldb_msg_add_string(msg, "classname", classname);
+       }
 
        ret = ldb_add(parentkd->ldb, msg);
+
+       talloc_free(msg);
+
        if (ret == LDB_ERR_ENTRY_ALREADY_EXISTS) {
                return WERR_ALREADY_EXISTS;
        }
@@ -693,13 +614,14 @@ static WERROR ldb_add_key(TALLOC_CTX *mem_ctx, const struct hive_key *parent,
                return WERR_FOOBAR;
        }
 
-       DEBUG(2, ("key added: %s\n", ldb_dn_get_linearized(msg->dn)));
+       DEBUG(2, ("key added: %s\n", ldb_dn_get_linearized(ldb_path)));
 
        newkd = talloc_zero(mem_ctx, struct ldb_key_data);
        W_ERROR_HAVE_NO_MEMORY(newkd);
        newkd->ldb = talloc_reference(newkd, parentkd->ldb);
        newkd->key.ops = &reg_backend_ldb;
-       newkd->dn = talloc_steal(newkd, msg->dn);
+       newkd->dn = talloc_steal(newkd, ldb_path);
+       newkd->classname = talloc_steal(newkd, classname);
 
        *newkey = (struct hive_key *)newkd;
 
@@ -710,33 +632,40 @@ static WERROR ldb_add_key(TALLOC_CTX *mem_ctx, const struct hive_key *parent,
        return WERR_OK;
 }
 
-static WERROR ldb_del_value (struct hive_key *key, const char *child)
+static WERROR ldb_del_value(TALLOC_CTX *mem_ctx, struct hive_key *key,
+                           const char *child)
 {
        int ret;
        struct ldb_key_data *kd = talloc_get_type(key, struct ldb_key_data);
-       TALLOC_CTX *mem_ctx;
        struct ldb_message *msg;
        struct ldb_dn *childdn;
 
-       if ((child == NULL) || (child[0] == '\0')) {
+       if (child[0] == '\0') {
                /* default value */
-               mem_ctx = talloc_init("ldb_del_value");
-
                msg = talloc_zero(mem_ctx, struct ldb_message);
                W_ERROR_HAVE_NO_MEMORY(msg);
                msg->dn = ldb_dn_copy(msg, kd->dn);
                W_ERROR_HAVE_NO_MEMORY(msg->dn);
-               ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
-               ldb_msg_add_empty(msg, "type", LDB_FLAG_MOD_DELETE, NULL);
+               ret = ldb_msg_add_empty(msg, "data", LDB_FLAG_MOD_DELETE, NULL);
+               if (ret != LDB_SUCCESS) {
+                       return WERR_FOOBAR;
+               }
+               ret = ldb_msg_add_empty(msg, "type", LDB_FLAG_MOD_DELETE,
+                                       NULL);
+               if (ret != LDB_SUCCESS) {
+                       return WERR_FOOBAR;
+               }
 
                ret = ldb_modify(kd->ldb, msg);
-               if (ret != LDB_SUCCESS) {
+
+               talloc_free(msg);
+
+               if (ret == LDB_ERR_NO_SUCH_ATTRIBUTE) {
+                       return WERR_BADFILE;
+               } else if (ret != LDB_SUCCESS) {
                        DEBUG(1, ("ldb_del_value: %s\n", ldb_errstring(kd->ldb)));
-                       talloc_free(mem_ctx);
                        return WERR_FOOBAR;
                }
-
-               talloc_free(mem_ctx);
        } else {
                /* normal value */
                childdn = ldb_dn_copy(kd->ldb, kd->dn);
@@ -766,13 +695,13 @@ static WERROR ldb_del_value (struct hive_key *key, const char *child)
        return WERR_OK;
 }
 
-static WERROR ldb_del_key(const struct hive_key *key, const char *name)
+static WERROR ldb_del_key(TALLOC_CTX *mem_ctx, const struct hive_key *key,
+                         const char *name)
 {
        unsigned int i;
        int ret;
        struct ldb_key_data *parentkd = talloc_get_type(key, struct ldb_key_data);
-       struct ldb_dn *ldap_path;
-       TALLOC_CTX *mem_ctx = talloc_init("ldb_del_key");
+       struct ldb_dn *ldb_path;
        struct ldb_context *c = parentkd->ldb;
        struct ldb_result *res_keys;
        struct ldb_result *res_vals;
@@ -782,32 +711,29 @@ static WERROR ldb_del_key(const struct hive_key *key, const char *name)
        /* Verify key exists by opening it */
        werr = ldb_open_key(mem_ctx, key, name, &hk);
        if (!W_ERROR_IS_OK(werr)) {
-               talloc_free(mem_ctx);
                return werr;
        }
 
-       ldap_path = reg_path_to_ldb(mem_ctx, key, name, NULL);
-       W_ERROR_HAVE_NO_MEMORY(ldap_path);
+       ldb_path = reg_path_to_ldb(mem_ctx, key, name, NULL);
+       W_ERROR_HAVE_NO_MEMORY(ldb_path);
 
        /* Search for subkeys */
-       ret = ldb_search(c, mem_ctx, &res_keys, ldap_path, LDB_SCOPE_ONELEVEL,
+       ret = ldb_search(c, mem_ctx, &res_keys, ldb_path, LDB_SCOPE_ONELEVEL,
                         NULL, "(key=*)");
 
        if (ret != LDB_SUCCESS) {
                DEBUG(0, ("Error getting subkeys for '%s': %s\n",
-                     ldb_dn_get_linearized(ldap_path), ldb_errstring(c)));
-               talloc_free(mem_ctx);
+                     ldb_dn_get_linearized(ldb_path), ldb_errstring(c)));
                return WERR_FOOBAR;
        }
 
        /* Search for values */
-       ret = ldb_search(c, mem_ctx, &res_vals, ldap_path, LDB_SCOPE_ONELEVEL,
+       ret = ldb_search(c, mem_ctx, &res_vals, ldb_path, LDB_SCOPE_ONELEVEL,
                         NULL, "(value=*)");
 
        if (ret != LDB_SUCCESS) {
                DEBUG(0, ("Error getting values for '%s': %s\n",
-                     ldb_dn_get_linearized(ldap_path), ldb_errstring(c)));
-               talloc_free(mem_ctx);
+                     ldb_dn_get_linearized(ldb_path), ldb_errstring(c)));
                return WERR_FOOBAR;
        }
 
@@ -816,7 +742,6 @@ static WERROR ldb_del_key(const struct hive_key *key, const char *name)
 
        if (ret != LDB_SUCCESS) {
                DEBUG(0, ("ldb_transaction_start: %s\n", ldb_errstring(c)));
-               talloc_free(mem_ctx);
                return WERR_FOOBAR;
        }
 
@@ -825,12 +750,12 @@ static WERROR ldb_del_key(const struct hive_key *key, const char *name)
                /* Delete any subkeys */
                for (i = 0; i < res_keys->count; i++)
                {
-                       werr = ldb_del_key(hk, ldb_msg_find_attr_as_string(
+                       werr = ldb_del_key(mem_ctx, hk,
+                                          ldb_msg_find_attr_as_string(
                                                        res_keys->msgs[i],
                                                        "key", NULL));
                        if (!W_ERROR_IS_OK(werr)) {
                                ret = ldb_transaction_cancel(c);
-                               talloc_free(mem_ctx);
                                return werr;
                        }
                }
@@ -838,25 +763,26 @@ static WERROR ldb_del_key(const struct hive_key *key, const char *name)
                /* Delete any values */
                for (i = 0; i < res_vals->count; i++)
                {
-                       werr = ldb_del_value(hk, ldb_msg_find_attr_as_string(
+                       werr = ldb_del_value(mem_ctx, hk,
+                                            ldb_msg_find_attr_as_string(
                                                        res_vals->msgs[i],
                                                        "value", NULL));
                        if (!W_ERROR_IS_OK(werr)) {
                                ret = ldb_transaction_cancel(c);
-                               talloc_free(mem_ctx);
                                return werr;
                        }
                }
        }
+       talloc_free(res_keys);
+       talloc_free(res_vals);
 
        /* Delete the key itself */
-       ret = ldb_delete(c, ldap_path);
+       ret = ldb_delete(c, ldb_path);
 
        if (ret != LDB_SUCCESS)
        {
                DEBUG(1, ("ldb_del_key: %s\n", ldb_errstring(c)));
                ret = ldb_transaction_cancel(c);
-               talloc_free(mem_ctx);
                return WERR_FOOBAR;
        }
 
@@ -867,12 +793,9 @@ static WERROR ldb_del_key(const struct hive_key *key, const char *name)
        {
                DEBUG(0, ("ldb_transaction_commit: %s\n", ldb_errstring(c)));
                ret = ldb_transaction_cancel(c);
-               talloc_free(mem_ctx);
                return WERR_FOOBAR;
        }
 
-       talloc_free(mem_ctx);
-
        /* reset cache */
        talloc_free(parentkd->subkeys);
        parentkd->subkeys = NULL;
@@ -896,7 +819,7 @@ static WERROR ldb_set_value(struct hive_key *parent,
        msg->dn = ldb_dn_copy(msg, kd->dn);
        W_ERROR_HAVE_NO_MEMORY(msg->dn);
 
-       if ((name != NULL) && (name[0] != '\0')) {
+       if (name[0] != '\0') {
                /* For a default value, we add/overwrite the attributes to/of the hive.
                   For a normal value, we create a new child. */
                if (!ldb_dn_add_child_fmt(msg->dn, "value=%s",
@@ -917,7 +840,7 @@ static WERROR ldb_set_value(struct hive_key *parent,
        if (ret == LDB_ERR_NO_SUCH_OBJECT) {
                i = 0;
                while (i < msg->num_elements) {
-                       if (msg->elements[i].flags == LDB_FLAG_MOD_DELETE) {
+                       if (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) == LDB_FLAG_MOD_DELETE) {
                                ldb_msg_remove_element(msg, &msg->elements[i]);
                        } else {
                                ++i;
@@ -930,6 +853,8 @@ static WERROR ldb_set_value(struct hive_key *parent,
                ret = LDB_SUCCESS;
        }
 
+       talloc_free(msg);
+
        if (ret != LDB_SUCCESS) {
                DEBUG(1, ("ldb_set_value: %s\n", ldb_errstring(kd->ldb)));
                talloc_free(mem_ctx);
@@ -955,6 +880,9 @@ static WERROR ldb_get_key_info(TALLOC_CTX *mem_ctx,
                               uint32_t *max_valbufsize)
 {
        struct ldb_key_data *kd = talloc_get_type(key, struct ldb_key_data);
+       uint32_t default_value_type = REG_NONE;
+       DATA_BLOB default_value = { NULL, 0 };
+       WERROR werr;
 
        /* Initialization */
        if (classname != NULL)
@@ -972,19 +900,36 @@ static WERROR ldb_get_key_info(TALLOC_CTX *mem_ctx,
        if (max_valbufsize != NULL)
                *max_valbufsize = 0;
 
+       /* We need this to get the default value (if it exists) for counting
+        * the values under the key and for finding out the longest value buffer
+        * size. If no default value exists the DATA_BLOB "default_value" will
+        * remain { NULL, 0 }. */
+       werr = ldb_get_default_value(mem_ctx, key, NULL, &default_value_type,
+                                    &default_value);
+       if ((!W_ERROR_IS_OK(werr)) && (!W_ERROR_EQUAL(werr, WERR_BADFILE))) {
+               return werr;
+       }
+
        if (kd->subkeys == NULL) {
                W_ERROR_NOT_OK_RETURN(cache_subkeys(kd));
        }
-
        if (kd->values == NULL) {
                W_ERROR_NOT_OK_RETURN(cache_values(kd));
        }
 
+       if (classname != NULL) {
+               *classname = kd->classname;
+       }
+
        if (num_subkeys != NULL) {
                *num_subkeys = kd->subkey_count;
        }
        if (num_values != NULL) {
                *num_values = kd->value_count;
+               /* also consider the default value if it exists */
+               if (default_value.data != NULL) {
+                       ++(*num_values);
+               }
        }
 
 
@@ -992,8 +937,6 @@ static WERROR ldb_get_key_info(TALLOC_CTX *mem_ctx,
                unsigned int i;
                struct ldb_message_element *el;
 
-               *max_subkeynamelen = 0;
-
                for (i = 0; i < kd->subkey_count; i++) {
                        el = ldb_msg_find_element(kd->subkeys[i], "key");
                        *max_subkeynamelen = MAX(*max_subkeynamelen, el->values[0].length);
@@ -1005,11 +948,11 @@ static WERROR ldb_get_key_info(TALLOC_CTX *mem_ctx,
                struct ldb_message_element *el;
                W_ERROR_NOT_OK_RETURN(cache_values(kd));
 
-               if (max_valbufsize != NULL)
-                       *max_valbufsize = 0;
-
-               if (max_valnamelen != NULL)
-                       *max_valnamelen = 0;
+               /* also consider the default value if it exists */
+               if ((max_valbufsize != NULL) && (default_value.data != NULL)) {
+                               *max_valbufsize = MAX(*max_valbufsize,
+                                                     default_value.length);
+               }
 
                for (i = 0; i < kd->value_count; i++) {
                        if (max_valnamelen != NULL) {
@@ -1029,6 +972,8 @@ static WERROR ldb_get_key_info(TALLOC_CTX *mem_ctx,
                }
        }
 
+       talloc_free(default_value.data);
+
        return WERR_OK;
 }