Merge branch 'v3-2-test' of ssh://jra@git.samba.org/data/git/samba into v3-2-test
[kai/samba.git] / source3 / registry / reg_api.c
index 085f52b33dc1bbda5f6a7c9579dca7ac4fd544f8..aba5735a0c730d44069867dd6a452a84823f6569 100644 (file)
@@ -2,6 +2,7 @@
  *  Unix SMB/CIFS implementation.
  *  Virtual Windows Registry Layer
  *  Copyright (C) Volker Lendecke 2006
+ *  Copyright (C) Michael Adam 2007-2008
  *
  *  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
  * 0x10                winreg_QueryInfoKey                     reg_queryinfokey
  * 0x11                winreg_QueryValue                       reg_queryvalue
  * 0x12                winreg_ReplaceKey
- * 0x13                winreg_RestoreKey
- * 0x14                winreg_SaveKey
+ * 0x13                winreg_RestoreKey                       reg_restorekey
+ * 0x14                winreg_SaveKey                          reg_savekey
  * 0x15                winreg_SetKeySecurity                   reg_setkeysecurity
  * 0x16                winreg_SetValue                         reg_setvalue
  * 0x17                winreg_UnLoadKey
  * 0x18                winreg_InitiateSystemShutdown
  * 0x19                winreg_AbortSystemShutdown
- * 0x1a                winreg_GetVersion
+ * 0x1a                winreg_GetVersion                       reg_getversion
  * 0x1b                winreg_OpenHKCC
  * 0x1c                winreg_OpenHKDD
  * 0x1d                winreg_QueryMultipleValues
@@ -62,6 +63,7 @@
  */
 
 #include "includes.h"
+#include "regfio.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_REGISTRY
@@ -182,7 +184,7 @@ static WERROR regkey_open_onelevel(TALLOC_CTX *mem_ctx,
        /* 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 "
+               DEBUG(0,("reg_open_onelevel: Failed to assign a "
                         "REGISTRY_HOOK to [%s]\n", key->name ));
                result = WERR_BADFILE;
                goto done;
@@ -452,7 +454,6 @@ WERROR reg_createkey(TALLOC_CTX *ctx, struct registry_key *parent,
        TALLOC_CTX *mem_ctx;
        char *path, *end;
        WERROR err;
-       REGSUBKEY_CTR *subkeys;
 
        if (!(mem_ctx = talloc_new(ctx))) return WERR_NOMEM;
 
@@ -516,11 +517,6 @@ WERROR reg_createkey(TALLOC_CTX *ctx, struct registry_key *parent,
         * Actually create the subkey
         */
 
-       if (!(subkeys = TALLOC_ZERO_P(mem_ctx, REGSUBKEY_CTR))) {
-               err = WERR_NOMEM;
-               goto done;
-       }
-
        err = fill_subkey_cache(create_parent);
        if (!W_ERROR_IS_OK(err)) goto done;
 
@@ -691,6 +687,338 @@ WERROR reg_setkeysecurity(struct registry_key *key,
        return regkey_set_secdesc(key->key, psecdesc);
 }
 
+WERROR reg_getversion(uint32_t *version)
+{
+       if (version == NULL) {
+               return WERR_INVALID_PARAM;
+       }
+
+       *version = 0x00000005; /* Windows 2000 registry API version */
+       return WERR_OK;
+}
+
+/*******************************************************************
+ Note: topkeypat is the *full* path that this *key will be
+ loaded into (including the name of the key)
+ ********************************************************************/
+
+static WERROR reg_load_tree(REGF_FILE *regfile, const char *topkeypath,
+                           REGF_NK_REC *key)
+{
+       REGF_NK_REC *subkey;
+       REGISTRY_KEY registry_key;
+       REGVAL_CTR *values;
+       REGSUBKEY_CTR *subkeys;
+       int i;
+       char *path = NULL;
+       WERROR result = WERR_OK;
+
+       /* initialize the REGISTRY_KEY structure */
+
+       registry_key.hook = reghook_cache_find(topkeypath);
+       if (!registry_key.hook) {
+               DEBUG(0, ("reg_load_tree: Failed to assigned a REGISTRY_HOOK "
+                         "to [%s]\n", topkeypath));
+               return WERR_BADFILE;
+       }
+
+       registry_key.name = talloc_strdup(regfile->mem_ctx, topkeypath);
+       if (!registry_key.name) {
+               DEBUG(0, ("reg_load_tree: Talloc failed for reg_key.name!\n"));
+               return WERR_NOMEM;
+       }
+
+       /* now start parsing the values and subkeys */
+
+       subkeys = TALLOC_ZERO_P(regfile->mem_ctx, REGSUBKEY_CTR);
+       if (subkeys == NULL) {
+               return WERR_NOMEM;
+       }
+
+       values = TALLOC_ZERO_P(subkeys, REGVAL_CTR);
+       if (values == NULL) {
+               return WERR_NOMEM;
+       }
+
+       /* copy values into the REGVAL_CTR */
+
+       for (i=0; i<key->num_values; i++) {
+               regval_ctr_addvalue(values, key->values[i].valuename,
+                                   key->values[i].type,
+                                   (char*)key->values[i].data,
+                                   (key->values[i].data_size & ~VK_DATA_IN_OFFSET));
+       }
+
+       /* copy subkeys into the REGSUBKEY_CTR */
+
+       key->subkey_index = 0;
+       while ((subkey = regfio_fetch_subkey( regfile, key ))) {
+               regsubkey_ctr_addkey(subkeys, subkey->keyname);
+       }
+
+       /* write this key and values out */
+
+       if (!store_reg_values(&registry_key, values)
+           || !store_reg_keys(&registry_key, subkeys))
+       {
+               DEBUG(0,("reg_load_tree: Failed to load %s!\n", topkeypath));
+               result = WERR_REG_IO_FAILURE;
+       }
+
+       TALLOC_FREE(subkeys);
+
+       if (!W_ERROR_IS_OK(result)) {
+               return result;
+       }
+
+       /* now continue to load each subkey registry tree */
+
+       key->subkey_index = 0;
+       while ((subkey = regfio_fetch_subkey(regfile, key))) {
+               path = talloc_asprintf(regfile->mem_ctx,
+                                      "%s\\%s",
+                                      topkeypath,
+                                      subkey->keyname);
+               if (path == NULL) {
+                       return WERR_NOMEM;
+               }
+               result = reg_load_tree(regfile, path, subkey);
+               if (!W_ERROR_IS_OK(result)) {
+                       break;
+               }
+       }
+
+       return result;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static WERROR restore_registry_key(REGISTRY_KEY *krecord, const char *fname)
+{
+       REGF_FILE *regfile;
+       REGF_NK_REC *rootkey;
+       WERROR result;
+
+       /* open the registry file....fail if the file already exists */
+
+       regfile = regfio_open(fname, (O_RDONLY), 0);
+       if (regfile == NULL) {
+               DEBUG(0, ("restore_registry_key: failed to open \"%s\" (%s)\n",
+                         fname, strerror(errno)));
+               return ntstatus_to_werror(map_nt_error_from_unix(errno));
+       }
+
+       /* get the rootkey from the regf file and then load the tree
+          via recursive calls */
+
+       if (!(rootkey = regfio_rootkey(regfile))) {
+               regfio_close(regfile);
+               return WERR_REG_FILE_INVALID;
+       }
+
+       result = reg_load_tree(regfile, krecord->name, rootkey);
+
+       /* cleanup */
+
+       regfio_close(regfile);
+
+       return result;
+}
+
+WERROR reg_restorekey(struct registry_key *key, const char *fname)
+{
+       return restore_registry_key(key->key, fname);
+}
+
+/********************************************************************
+********************************************************************/
+
+static WERROR reg_write_tree(REGF_FILE *regfile, const char *keypath,
+                            REGF_NK_REC *parent, SEC_DESC *sec_desc)
+{
+       REGF_NK_REC *key;
+       REGVAL_CTR *values;
+       REGSUBKEY_CTR *subkeys;
+       int i, num_subkeys;
+       char *key_tmp = NULL;
+       char *keyname, *parentpath;
+       char *subkeypath = NULL;
+       char *subkeyname;
+       REGISTRY_KEY registry_key;
+       WERROR result = WERR_OK;
+
+       if (!regfile) {
+               return WERR_GENERAL_FAILURE;
+       }
+
+       if (!keypath) {
+               return WERR_OBJECT_PATH_INVALID;
+       }
+
+       /* split up the registry key path */
+
+       key_tmp = talloc_strdup(regfile->mem_ctx, keypath);
+       if (!key_tmp) {
+               return WERR_NOMEM;
+       }
+       if (!reg_split_key(key_tmp, &parentpath, &keyname)) {
+               return WERR_OBJECT_PATH_INVALID;
+       }
+
+       if (!keyname) {
+               keyname = parentpath;
+       }
+
+       /* we need a REGISTRY_KEY object here to enumerate subkeys and values */
+
+       ZERO_STRUCT(registry_key);
+
+       registry_key.name = talloc_strdup(regfile->mem_ctx, keypath);
+       if (registry_key.name == NULL) {
+               return WERR_NOMEM;
+       }
+
+       registry_key.hook = reghook_cache_find(registry_key.name);
+       if (registry_key.hook == NULL) {
+               return WERR_BADFILE;
+       }
+
+       /* lookup the values and subkeys */
+
+       subkeys = TALLOC_ZERO_P(regfile->mem_ctx, REGSUBKEY_CTR);
+       if (subkeys == NULL) {
+               return WERR_NOMEM;
+       }
+
+       values = TALLOC_ZERO_P(subkeys, REGVAL_CTR);
+       if (values == NULL) {
+               return WERR_NOMEM;
+       }
+
+       fetch_reg_keys(&registry_key, subkeys);
+       fetch_reg_values(&registry_key, values);
+
+       /* write out this key */
+
+       key = regfio_write_key(regfile, keyname, values, subkeys, sec_desc,
+                              parent);
+       if (key == NULL) {
+               result = WERR_CAN_NOT_COMPLETE;
+               goto done;
+       }
+
+       /* write each one of the subkeys out */
+
+       num_subkeys = regsubkey_ctr_numkeys(subkeys);
+       for (i=0; i<num_subkeys; i++) {
+               subkeyname = regsubkey_ctr_specific_key(subkeys, i);
+               subkeypath = talloc_asprintf(regfile->mem_ctx, "%s\\%s",
+                                            keypath, subkeyname);
+               if (subkeypath == NULL) {
+                       result = WERR_NOMEM;
+                       goto done;
+               }
+               result = reg_write_tree(regfile, subkeypath, key, sec_desc);
+               if (!W_ERROR_IS_OK(result))
+                       goto done;
+       }
+
+       DEBUG(6, ("reg_write_tree: wrote key [%s]\n", keypath));
+
+done:
+       TALLOC_FREE(subkeys);
+       TALLOC_FREE(registry_key.name);
+
+       return result;
+}
+
+static const struct generic_mapping reg_generic_map =
+       { REG_KEY_READ, REG_KEY_WRITE, REG_KEY_EXECUTE, REG_KEY_ALL };
+
+static WERROR make_default_reg_sd(TALLOC_CTX *ctx, SEC_DESC **psd)
+{
+       DOM_SID adm_sid, owner_sid;
+       SEC_ACE ace[2];         /* at most 2 entries */
+       SEC_ACCESS mask;
+       SEC_ACL *psa = NULL;
+       size_t sd_size;
+
+       /* set the owner to BUILTIN\Administrator */
+
+       sid_copy(&owner_sid, &global_sid_Builtin);
+       sid_append_rid(&owner_sid, DOMAIN_USER_RID_ADMIN );
+       
+
+       /* basic access for Everyone */
+
+       init_sec_access(&mask, reg_generic_map.generic_execute
+                              | reg_generic_map.generic_read);
+       init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED,
+                    mask, 0);
+
+       /* add Full Access 'BUILTIN\Administrators' */
+
+       init_sec_access(&mask, reg_generic_map.generic_all);
+       sid_copy(&adm_sid, &global_sid_Builtin);
+       sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS);
+       init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
+
+       /* create the security descriptor */
+
+       psa = make_sec_acl(ctx, NT4_ACL_REVISION, 2, ace);
+       if (psa == NULL) {
+               return WERR_NOMEM;
+       }
+
+       *psd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1,
+                            SEC_DESC_SELF_RELATIVE, &owner_sid, NULL,
+                            NULL, psa, &sd_size);
+       if (*psd == NULL) {
+               return WERR_NOMEM;
+       }
+
+       return WERR_OK;
+}
+
+static WERROR backup_registry_key(REGISTRY_KEY *krecord, const char *fname)
+{
+       REGF_FILE *regfile;
+       WERROR result;
+       SEC_DESC *sd = NULL;
+
+       /* open the registry file....fail if the file already exists */
+
+       regfile = regfio_open(fname, (O_RDWR|O_CREAT|O_EXCL),
+                             (S_IREAD|S_IWRITE));
+       if (regfile == NULL) {
+               DEBUG(0,("backup_registry_key: failed to open \"%s\" (%s)\n",
+                        fname, strerror(errno) ));
+               return ntstatus_to_werror(map_nt_error_from_unix(errno));
+       }
+
+       result = make_default_reg_sd(regfile->mem_ctx, &sd);
+       if (!W_ERROR_IS_OK(result)) {
+               regfio_close(regfile);
+               return result;
+       }
+
+       /* write the registry tree to the file  */
+
+       result = reg_write_tree(regfile, krecord->name, NULL, sd);
+
+       /* cleanup */
+
+       regfio_close(regfile);
+
+       return result;
+}
+
+WERROR reg_savekey(struct registry_key *key, const char *fname)
+{
+       return backup_registry_key(key->key, fname);
+}
+
 /**********************************************************************
  * Higher level utility functions
  **********************************************************************/
@@ -721,9 +1049,7 @@ WERROR reg_deleteallvalues(struct registry_key *key)
 }
 
 /*
- * Utility function to open a complete registry path including the hive
- * prefix. This should become the replacement function for
- * regkey_open_internal.
+ * Utility function to open a complete registry path including the hive prefix.
  */
 
 WERROR reg_open_path(TALLOC_CTX *mem_ctx, const char *orig_path,