r23801: The FSF has moved around a lot. This fixes their Mass Ave address.
[tprouty/samba.git] / source3 / registry / reg_api.c
index 7abf0538f8e2d6f71ea6cd8d4a9216febf97b69c..b7f79c151914e53ac58f1538e19284d6c33ce957 100644 (file)
@@ -5,7 +5,7 @@
  *
  *  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,
@@ -14,8 +14,7 @@
  *  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/>.
  */
 
 /* Attempt to wrap the existing API in a more winreg.idl-like way */
@@ -57,86 +56,182 @@ static WERROR fill_subkey_cache(struct registry_key *key)
        return WERR_OK;
 }
 
-WERROR reg_openhive(TALLOC_CTX *mem_ctx, const char *hive,
-                   uint32 desired_access,
-                   const struct nt_user_token *token,
-                   struct registry_key **pkey)
+static int regkey_destructor(REGISTRY_KEY *key)
 {
-       struct registry_key *key;
-       WERROR err;
+       return regdb_close();
+}
 
-       SMB_ASSERT(hive[0] != '\0');
-       SMB_ASSERT(strchr(hive, '\\') == NULL);
+static WERROR regkey_open_onelevel(TALLOC_CTX *mem_ctx, 
+                                  struct registry_key *parent,
+                                  const char *name,
+                                  const struct nt_user_token *token,
+                                  uint32 access_desired,
+                                  struct registry_key **pregkey)
+{
+       WERROR          result = WERR_OK;
+       struct registry_key *regkey;
+       REGISTRY_KEY *key;
+       REGSUBKEY_CTR   *subkeys = NULL;
 
-       if (!(key = TALLOC_ZERO_P(mem_ctx, struct registry_key))) {
-               return WERR_NOMEM;
+       DEBUG(7,("regkey_open_onelevel: name = [%s]\n", name));
+
+       SMB_ASSERT(strchr(name, '\\') == NULL);
+
+       if (!(regkey = TALLOC_ZERO_P(mem_ctx, struct registry_key)) ||
+           !(regkey->token = dup_nt_token(regkey, token)) ||
+           !(regkey->key = TALLOC_ZERO_P(regkey, REGISTRY_KEY))) {
+               result = WERR_NOMEM;
+               goto done;
        }
 
-       if (!(key->token = dup_nt_token(key, token))) {
-               TALLOC_FREE(key);
-               return WERR_NOMEM;
+       if ( !(W_ERROR_IS_OK(result = regdb_open())) ) {
+               goto done;
        }
 
-       err = regkey_open_internal(key, &key->key, hive, token,
-                                  desired_access);
+       key = regkey->key;
+       talloc_set_destructor(key, regkey_destructor);
+               
+       /* initialization */
+       
+       key->type = REG_KEY_GENERIC;
 
-       if (!W_ERROR_IS_OK(err)) {
-               TALLOC_FREE(key);
-               return err;
+       if (name[0] == '\0') {
+               /*
+                * Open a copy of the parent key
+                */
+               if (!parent) {
+                       result = WERR_BADFILE;
+                       goto done;
+               }
+               key->name = talloc_strdup(key, parent->key->name);
+       }
+       else {
+               /*
+                * Normal subkey open
+                */
+               key->name = talloc_asprintf(key, "%s%s%s",
+                                           parent ? parent->key->name : "",
+                                           parent ? "\\": "",
+                                           name);
        }
 
-       *pkey = key;
-       return WERR_OK;
+       if (key->name == NULL) {
+               result = WERR_NOMEM;
+               goto done;
+       }
+
+       /* Tag this as a Performance Counter Key */
+
+       if( StrnCaseCmp(key->name, KEY_HKPD, strlen(KEY_HKPD)) == 0 )
+               key->type = REG_KEY_HKPD;
+       
+       /* Look up the table of registry I/O operations */
 
+       if ( !(key->hook = reghook_cache_find( key->name )) ) {
+               DEBUG(0,("reg_open_onelevel: Failed to assigned a "
+                        "REGISTRY_HOOK to [%s]\n", key->name ));
+               result = WERR_BADFILE;
+               goto done;
+       }
+       
+       /* check if the path really exists; failed is indicated by -1 */
+       /* if the subkey count failed, bail out */
+
+       if ( !(subkeys = TALLOC_ZERO_P( key, REGSUBKEY_CTR )) ) {
+               result = WERR_NOMEM;
+               goto done;
+       }
+
+       if ( fetch_reg_keys( key, subkeys ) == -1 )  {
+               result = WERR_BADFILE;
+               goto done;
+       }
+       
+       TALLOC_FREE( subkeys );
+
+       if ( !regkey_access_check( key, access_desired, &key->access_granted,
+                                  token ) ) {
+               result = WERR_ACCESS_DENIED;
+               goto done;
+       }
+
+       *pregkey = regkey;
+       result = WERR_OK;
+       
+done:
+       if ( !W_ERROR_IS_OK(result) ) {
+               TALLOC_FREE(regkey);
+       }
+
+       return result;
+}
+
+WERROR reg_openhive(TALLOC_CTX *mem_ctx, const char *hive,
+                   uint32 desired_access,
+                   const struct nt_user_token *token,
+                   struct registry_key **pkey)
+{
+       SMB_ASSERT(hive != NULL);
+       SMB_ASSERT(hive[0] != '\0');
+       SMB_ASSERT(strchr(hive, '\\') == NULL);
+
+       return regkey_open_onelevel(mem_ctx, NULL, hive, token, desired_access,
+                                   pkey);
 }
 
 WERROR reg_openkey(TALLOC_CTX *mem_ctx, struct registry_key *parent,
                   const char *name, uint32 desired_access,
                   struct registry_key **pkey)
 {
-       struct registry_key *key;
+       struct registry_key *direct_parent = parent;
        WERROR err;
-       char *path;
+       char *p, *path, *to_free;
+       size_t len;
 
-       if (!(key = TALLOC_ZERO_P(mem_ctx, struct registry_key))) {
+       if (!(path = SMB_STRDUP(name))) {
                return WERR_NOMEM;
        }
+       to_free = path;
 
-       if (!(key->token = dup_nt_token(key, parent->token))) {
-               TALLOC_FREE(key);
-               return WERR_NOMEM;
-       }
+       len = strlen(path);
 
-       if (name[0] == '\0') {
-               /*
-                * Make a copy of the parent
-                */ 
-               path = talloc_strdup(key, parent->key->name);
-       }
-       else {
-               /*
-                * Normal subpath open
-                */
-               path = talloc_asprintf(key, "%s\\%s", parent->key->name,
-                                      name);
+       if ((len > 0) && (path[len-1] == '\\')) {
+               path[len-1] = '\0';
        }
 
-       if (!path) {
-               TALLOC_FREE(key);
-               return WERR_NOMEM;
-       }
+       while ((p = strchr(path, '\\')) != NULL) {
+               char *name_component;
+               struct registry_key *tmp;
 
-       err = regkey_open_internal(key, &key->key, path, parent->token,
-                                  desired_access);
-       TALLOC_FREE(path);
+               if (!(name_component = SMB_STRNDUP(path, (p - path)))) {
+                       err = WERR_NOMEM;
+                       goto error;
+               }
 
-       if (!W_ERROR_IS_OK(err)) {
-               TALLOC_FREE(key);
-               return err;
+               err = regkey_open_onelevel(mem_ctx, direct_parent,
+                                          name_component, parent->token,
+                                          SEC_RIGHTS_ENUM_SUBKEYS, &tmp);
+               SAFE_FREE(name_component);
+
+               if (!W_ERROR_IS_OK(err)) {
+                       goto error;
+               }
+               if (direct_parent != parent) {
+                       TALLOC_FREE(direct_parent);
+               }
+
+               direct_parent = tmp;
+               path = p+1;
        }
 
-       *pkey = key;
-       return WERR_OK;
+       err = regkey_open_onelevel(mem_ctx, direct_parent, path, parent->token,
+                                  desired_access, pkey);
+ error:
+       if (direct_parent != parent) {
+               TALLOC_FREE(direct_parent);
+       }
+       SAFE_FREE(to_free);
+       return err;
 }
 
 WERROR reg_enumkey(TALLOC_CTX *mem_ctx, struct registry_key *key,
@@ -182,7 +277,7 @@ WERROR reg_enumvalue(TALLOC_CTX *mem_ctx, struct registry_key *key,
        }
 
        if (idx >= key->values->num_values) {
-               return WERR_BADFILE;
+               return WERR_NO_MORE_ITEMS;
        }
 
        err = registry_pull_value(mem_ctx, &val,
@@ -400,6 +495,7 @@ WERROR reg_deletekey(struct registry_key *parent, const char *path)
        TALLOC_CTX *mem_ctx;
        char *name, *end;
        int num_subkeys;
+       struct registry_key *tmp_key;
 
        if (!(mem_ctx = talloc_init("reg_createkey"))) return WERR_NOMEM;
 
@@ -408,18 +504,30 @@ WERROR reg_deletekey(struct registry_key *parent, const char *path)
                goto error;
        }
 
-       if ((end = strrchr(name, '\\')) != NULL) {
-               struct registry_key *tmp;
+       /* check if the key has subkeys */
+       err = reg_openkey(mem_ctx, parent, name, REG_KEY_READ, &tmp_key);
+       if (!W_ERROR_IS_OK(err)) {
+               goto error;
+       }
+       if (!W_ERROR_IS_OK(err = fill_subkey_cache(tmp_key))) {
+               goto error;
+       }
+       if (tmp_key->subkeys->num_subkeys > 0) {
+               err = WERR_ACCESS_DENIED;
+               goto error;
+       }
 
+       /* no subkeys - proceed with delete */
+       if ((end = strrchr(name, '\\')) != NULL) {
                *end = '\0';
 
                err = reg_openkey(mem_ctx, parent, name,
-                                 SEC_RIGHTS_CREATE_SUBKEY, &tmp);
+                                 SEC_RIGHTS_CREATE_SUBKEY, &tmp_key);
                if (!W_ERROR_IS_OK(err)) {
                        goto error;
                }
 
-               parent = tmp;
+               parent = tmp_key;
                name = end+1;
        }
 
@@ -510,3 +618,110 @@ WERROR reg_deletevalue(struct registry_key *key, const char *name)
        return WERR_OK;
 }
 
+
+/*
+ * Utility function to open a complete registry path including the hive
+ * prefix. This should become the replacement function for
+ * regkey_open_internal.
+ */
+
+WERROR reg_open_path(TALLOC_CTX *mem_ctx, const char *orig_path,
+                    uint32 desired_access, const struct nt_user_token *token,
+                    struct registry_key **pkey)
+{
+       struct registry_key *hive, *key;
+       char *path, *p;
+       WERROR err;
+
+       if (!(path = SMB_STRDUP(orig_path))) {
+               return WERR_NOMEM;
+       }
+
+       p = strchr(path, '\\');
+
+       if ((p == NULL) || (p[1] == '\0')) {
+               /*
+                * No key behind the hive, just return the hive
+                */
+
+               err = reg_openhive(mem_ctx, path, desired_access, token,
+                                  &hive);
+               if (!W_ERROR_IS_OK(err)) {
+                       SAFE_FREE(path);
+                       return err;
+               }
+               SAFE_FREE(path);
+               *pkey = hive;
+               return WERR_OK;
+       }
+
+       *p = '\0';
+
+       err = reg_openhive(mem_ctx, path, SEC_RIGHTS_ENUM_SUBKEYS, token,
+                          &hive);
+       if (!W_ERROR_IS_OK(err)) {
+               SAFE_FREE(path);
+               return err;
+       }
+
+       err = reg_openkey(mem_ctx, hive, p+1, desired_access, &key);
+
+       TALLOC_FREE(hive);
+       SAFE_FREE(path);
+
+       if (!W_ERROR_IS_OK(err)) {
+               return err;
+       }
+
+       *pkey = key;
+       return WERR_OK;
+}
+
+
+/*
+ * Utility function to delete a registry key with all its subkeys. 
+ * Note that reg_deletekey returns ACCESS_DENIED when called on a 
+ * key that has subkeys.
+ */
+WERROR reg_deletekey_recursive(TALLOC_CTX *ctx,
+                              struct registry_key *parent, 
+                              const char *path)
+{
+       TALLOC_CTX *mem_ctx = NULL;
+       WERROR werr = WERR_OK;
+       struct registry_key *key;
+       char *subkey_name = NULL;
+
+       mem_ctx = talloc_new(ctx);
+       if (mem_ctx == NULL) {
+               werr = WERR_NOMEM;
+               goto done;
+       }
+
+       /* recurse through subkeys first */
+       werr = reg_openkey(mem_ctx, parent, path, REG_KEY_WRITE, &key);
+       if (!W_ERROR_IS_OK(werr)) {
+               goto done;
+       }
+
+       while (W_ERROR_IS_OK(werr = reg_enumkey(mem_ctx, key, 0,
+                                               &subkey_name, NULL))) 
+       {
+               werr = reg_deletekey_recursive(mem_ctx, key, subkey_name);
+               if (!W_ERROR_IS_OK(werr)) {
+                       goto done;
+               }
+       }
+       if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) {
+               DEBUG(1, ("reg_deletekey_recursive: Error enumerating "
+                         "subkeys: %s\n", dos_errstr(werr)));
+               goto done;
+       }
+
+       /* now delete the actual key */
+       werr = reg_deletekey(parent, path);
+       
+done:
+       TALLOC_FREE(mem_ctx);
+       return werr;
+}