r10656: BIG merge from trunk. Features not copied over
[vlendec/samba-autobuild/.git] / source3 / registry / reg_frontend.c
index 05bcd989b445e18bb02e3bfbc69b82435111018e..f41c5885bc4d7bdc8710793790ae8ab4e3336c93 100644 (file)
@@ -1,7 +1,7 @@
 /* 
  *  Unix SMB/CIFS implementation.
- *  RPC Pipe client / server routines
- *  Copyright (C) Gerald Carter                     2002.
+ *  Virtual Windows Registry Layer
+ *  Copyright (C) Gerald Carter                     2002-2005
  *
  *  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
 #define DBGC_CLASS DBGC_RPC_SRV
 
 extern REGISTRY_OPS printing_ops;
+extern REGISTRY_OPS eventlog_ops;
+extern REGISTRY_OPS shares_reg_ops;
 extern REGISTRY_OPS regdb_ops;         /* these are the default */
 
 /* array of REGISTRY_HOOK's which are read into a tree for easy access */
-
+/* #define REG_TDB_ONLY                1 */
 
 REGISTRY_HOOK reg_hooks[] = {
-  { KEY_PRINTING,   &printing_ops },
+#ifndef REG_TDB_ONLY 
+  { KEY_PRINTING,              &printing_ops },
+  { KEY_PRINTING_2K,           &printing_ops },
+  { KEY_PRINTING_PORTS,        &printing_ops },
+  { KEY_EVENTLOG,              &eventlog_ops }, 
+  { KEY_SHARES,                &shares_reg_ops },
+#endif
   { NULL, NULL }
 };
 
 
-/*
- * Utility functions for REGSUBKEY_CTR
- */
+static struct generic_mapping reg_generic_map = 
+       { REG_KEY_READ, REG_KEY_WRITE, REG_KEY_EXECUTE, REG_KEY_ALL };
 
-/***********************************************************************
- Init the talloc context held by a REGSUBKEY_CTR structure
- **********************************************************************/
+/********************************************************************
+********************************************************************/
 
-void regsubkey_ctr_init( REGSUBKEY_CTR *ctr )
+static NTSTATUS registry_access_check( SEC_DESC *sec_desc, NT_USER_TOKEN *token, 
+                                     uint32 access_desired, uint32 *access_granted )
 {
-       if ( !ctr->ctx )
-               ctr->ctx = talloc_init();
-}
+       NTSTATUS result;
 
-/***********************************************************************
- Add a new key to the array
- **********************************************************************/
-
-int regsubkey_ctr_addkey( REGSUBKEY_CTR *ctr, char *keyname )
-{
-       uint32 len;
-       char **pp;
-       
-       if ( keyname )
-       {
-               len = strlen( keyname );
-
-               /* allocate a space for the char* in the array */
-               
-               if (  ctr->subkeys == 0 )
-                       ctr->subkeys = talloc( ctr->ctx, sizeof(char*) );
-               else {
-                       pp = talloc_realloc( ctr->ctx, ctr->subkeys, sizeof(char*)*(ctr->num_subkeys+1) );
-                       if ( pp )
-                               ctr->subkeys = pp;
-               }
-
-               /* allocate the string and save it in the array */
-               
-               ctr->subkeys[ctr->num_subkeys] = talloc( ctr->ctx, len+1 );
-               strncpy( ctr->subkeys[ctr->num_subkeys], keyname, len+1 );
-               ctr->num_subkeys++;
+       if ( geteuid() == sec_initial_uid() ) {
+               DEBUG(5,("registry_access_check: using root's token\n"));
+               token = get_root_nt_token();
        }
-       
-       return ctr->num_subkeys;
-}
-/***********************************************************************
- How many keys does the container hold ?
- **********************************************************************/
-
-int regsubkey_ctr_numkeys( REGSUBKEY_CTR *ctr )
-{
-       return ctr->num_subkeys;
-}
 
-/***********************************************************************
- Retreive a specific key string
- **********************************************************************/
-
-char* regsubkey_ctr_specific_key( REGSUBKEY_CTR *ctr, uint32 key_index )
-{
-       if ( ! (key_index < ctr->num_subkeys) )
-               return NULL;
-               
-       return ctr->subkeys[key_index];
-}
+       se_map_generic( &access_desired, &reg_generic_map );
+       se_access_check( sec_desc, token, access_desired, access_granted, &result );
 
-/***********************************************************************
- free memory held by a REGSUBKEY_CTR structure
- **********************************************************************/
-
-void regsubkey_ctr_destroy( REGSUBKEY_CTR *ctr )
-{
-       if ( ctr ) {
-               talloc_destroy( ctr->ctx );     
-               ZERO_STRUCTP( ctr );
-       }
+       return result;
 }
 
+/********************************************************************
+********************************************************************/
 
-/*
- * Utility functions for REGVAL_CTR
- */
-
-/***********************************************************************
- Init the talloc context held by a REGSUBKEY_CTR structure
- **********************************************************************/
-
-void regval_ctr_init( REGVAL_CTR *ctr )
+static SEC_DESC* construct_registry_sd( TALLOC_CTX *ctx )
 {
-       if ( !ctr->ctx )
-               ctr->ctx = talloc_init();
-}
+       SEC_ACE ace[2]; 
+       SEC_ACCESS mask;
+       size_t i = 0;
+       SEC_DESC *sd;
+       SEC_ACL *acl;
+       size_t sd_size;
 
-/***********************************************************************
- How many keys does the container hold ?
- **********************************************************************/
-
-int regval_ctr_numvals( REGVAL_CTR *ctr )
-{
-       return ctr->num_values;
-}
-
-/***********************************************************************
- allocate memory for and duplicate a REGISTRY_VALUE.
- This is malloc'd memory so the caller should free it when done
- **********************************************************************/
-
-REGISTRY_VALUE* dup_registry_value( REGISTRY_VALUE *val )
-{
-       REGISTRY_VALUE  *copy = NULL;
+       /* basic access for Everyone */
        
-       if ( !val )
-               return NULL;
+       init_sec_access(&mask, REG_KEY_READ );
+       init_sec_ace(&ace[i++], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
        
-       if ( !(copy = malloc( sizeof(REGISTRY_VALUE) )) ) {
-               DEBUG(0,("dup_registry_value: malloc() failed!\n"));
-               return NULL;
-       }
+       /* Full Access 'BUILTIN\Administrators' */
        
-       /* copy all the non-pointer initial data */
+       init_sec_access(&mask, REG_KEY_ALL );
+       init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0);
        
-       memcpy( copy, val, sizeof(REGISTRY_VALUE) );
-       if ( val->data_p ) 
-       {
-               if ( !(copy->data_p = memdup( val->data_p, val->size )) ) {
-                       DEBUG(0,("dup_registry_value: memdup() failed for [%d] bytes!\n",
-                               val->size));
-                       SAFE_FREE( copy );
-               }
-       }
        
-       return copy;    
-}
-
-/**********************************************************************
- free the memory allocated to a REGISTRY_VALUE 
- *********************************************************************/
-void free_registry_value( REGISTRY_VALUE *val )
-{
-       if ( !val )
-               return;
-               
-       SAFE_FREE( val->data_p );
-       SAFE_FREE( val );
+       /* create the security descriptor */
        
-       return;
-}
-
-/**********************************************************************
- *********************************************************************/
-
-uint8* regval_data_p( REGISTRY_VALUE *val )
-{
-       return val->data_p;
-}
-
-/**********************************************************************
- *********************************************************************/
-
-int regval_size( REGISTRY_VALUE *val )
-{
-       return val->size;
-}
-
-/**********************************************************************
- *********************************************************************/
-
-char* regval_name( REGISTRY_VALUE *val )
-{
-       return val->valuename;
-}
-
-/**********************************************************************
- *********************************************************************/
-
-uint32 regval_type( REGISTRY_VALUE *val )
-{
-       return val->type;
-}
-
-/***********************************************************************
- Retreive a pointer to a specific value.  Caller shoud dup the structure
- since this memory may go away with a regval_ctr_destroy()
- **********************************************************************/
-
-REGISTRY_VALUE* regval_ctr_specific_value( REGVAL_CTR *ctr, uint32 idx )
-{
-       if ( !(idx < ctr->num_values) )
+       if ( !(acl = make_sec_acl(ctx, NT4_ACL_REVISION, i, ace)) )
                return NULL;
-               
-       return ctr->values[idx];
-}
 
-/***********************************************************************
- Retrive the TALLOC_CTX associated with a REGISTRY_VALUE 
- **********************************************************************/
-
-TALLOC_CTX* regval_ctr_getctx( REGVAL_CTR *val )
-{
-       if ( !val )
+       if ( !(sd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, acl, &sd_size)) )
                return NULL;
 
-       return val->ctx;
-}
-
-/***********************************************************************
- Add a new registry value to the array
- **********************************************************************/
-
-int regval_ctr_addvalue( REGVAL_CTR *ctr, char *name, uint16 type, 
-                         char *data_p, size_t size )
-{
-       REGISTRY_VALUE **ppreg;
-       
-       if ( name )
-       {
-               /* allocate a slot in the array of pointers */
-               
-               if (  ctr->num_values == 0 )
-                       ctr->values = talloc( ctr->ctx, sizeof(REGISTRY_VALUE*) );
-               else {
-                       ppreg = talloc_realloc( ctr->ctx, ctr->values, sizeof(REGISTRY_VALUE*)*(ctr->num_values+1) );
-                       if ( ppreg )
-                               ctr->values = ppreg;
-               }
-
-               /* allocate a new value and store the pointer in the arrya */
-               
-               ctr->values[ctr->num_values] = talloc( ctr->ctx, sizeof(REGISTRY_VALUE) );
-
-               /* init the value */
-       
-               fstrcpy( ctr->values[ctr->num_values]->valuename, name );
-               ctr->values[ctr->num_values]->type = type;
-               ctr->values[ctr->num_values]->data_p = talloc_memdup( ctr->ctx, data_p, size );
-               ctr->values[ctr->num_values]->size = size;
-               ctr->num_values++;
-       }
-
-       return ctr->num_values;
-}
-
-/***********************************************************************
- Add a new registry value to the array
- **********************************************************************/
-
-int regval_ctr_copyvalue( REGVAL_CTR *ctr, REGISTRY_VALUE *val )
-{
-       REGISTRY_VALUE **ppreg;
-       
-       if ( val )
-       {
-               /* allocate a slot in the array of pointers */
-               
-               if (  ctr->num_values == 0 )
-                       ctr->values = talloc( ctr->ctx, sizeof(REGISTRY_VALUE*) );
-               else {
-                       ppreg = talloc_realloc( ctr->ctx, ctr->values, sizeof(REGISTRY_VALUE*)*(ctr->num_values+1) );
-                       if ( ppreg )
-                               ctr->values = ppreg;
-               }
-
-               /* allocate a new value and store the pointer in the arrya */
-               
-               ctr->values[ctr->num_values] = talloc( ctr->ctx, sizeof(REGISTRY_VALUE) );
-
-               /* init the value */
-       
-               fstrcpy( ctr->values[ctr->num_values]->valuename, val->valuename );
-               ctr->values[ctr->num_values]->type = val->type;
-               ctr->values[ctr->num_values]->data_p = talloc_memdup( ctr->ctx, val->data_p, val->size );
-               ctr->values[ctr->num_values]->size = val->size;
-               ctr->num_values++;
-       }
-
-       return ctr->num_values;
+       return sd;
 }
 
-/***********************************************************************
- Delete a single value from the registry container.
- No need to free memory since it is talloc'd.
- **********************************************************************/
-
-int regval_ctr_delvalue( REGVAL_CTR *ctr, char *name )
-{
-       int     i;
-       
-       /* search for the value */
-       
-       for ( i=0; i<ctr->num_values; i++ ) {
-               if ( strcmp( ctr->values[i]->valuename, name ) == 0)
-                       break;
-       }
-       
-       /* just return if we don't find it */
-       
-       if ( i == ctr->num_values )
-               return ctr->num_values;
-       
-       /* just shift everything down one */
-       
-       for ( /* use previous i */; i<(ctr->num_values-1); i++ )
-               memcpy( ctr->values[i], ctr->values[i+1], sizeof(REGISTRY_VALUE) );
-               
-       /* paranoia */
-       
-       ZERO_STRUCTP( ctr->values[i] );
-       
-       ctr->num_values--;
-       
-       return ctr->num_values;
-}
-
-/***********************************************************************
- Delete a single value from the registry container.
- No need to free memory since it is talloc'd.
- **********************************************************************/
-
-REGISTRY_VALUE* regval_ctr_getvalue( REGVAL_CTR *ctr, char *name )
-{
-       int     i;
-       
-       /* search for the value */
-       
-       for ( i=0; i<ctr->num_values; i++ ) {
-               if ( strequal( ctr->values[i]->valuename, name ) )
-                       return ctr->values[i];
-       }
-       
-       return NULL;
-}
-
-/***********************************************************************
- free memory held by a REGVAL_CTR structure
- **********************************************************************/
-
-void regval_ctr_destroy( REGVAL_CTR *ctr )
-{
-       if ( ctr ) {
-               talloc_destroy( ctr->ctx );
-               ZERO_STRUCTP( ctr );
-       }
-}
 
 /***********************************************************************
  Open the registry database and initialize the REGISTRY_HOOK cache
@@ -392,11 +110,12 @@ BOOL init_registry( void )
 {
        int i;
        
+       
        if ( !init_registry_db() ) {
                DEBUG(0,("init_registry: failed to initialize the registry tdb!\n"));
                return False;
        }
-               
+
        /* build the cache tree of registry hooks */
        
        reghook_cache_init();
@@ -409,11 +128,16 @@ BOOL init_registry( void )
        if ( DEBUGLEVEL >= 20 )
                reghook_dump_cache(20);
 
-       return True;
-}
+       /* inform the external eventlog machinery of the change */
+
+       eventlog_refresh_external_parameters( get_root_nt_token() );
 
+       /* add any services keys */
 
+       svcctl_init_keys();
 
+       return True;
+}
 
 /***********************************************************************
  High level wrapper function for storing registry subkeys
@@ -421,10 +145,10 @@ BOOL init_registry( void )
  
 BOOL store_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkeys )
 {
-       if ( key->hook && key->hook->ops && key->hook->ops->store_subkeys_fn )
-               return key->hook->ops->store_subkeys_fn( key->name, subkeys );
-       else
-               return False;
+       if ( key->hook && key->hook->ops && key->hook->ops->store_subkeys )
+               return key->hook->ops->store_subkeys( key->name, subkeys );
+               
+       return False;
 
 }
 
@@ -434,10 +158,13 @@ BOOL store_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkeys )
  
 BOOL store_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
 {
-       if ( key->hook && key->hook->ops && key->hook->ops->store_values_fn )
-               return key->hook->ops->store_values_fn( key->name, val );
-       else
+       if ( check_dynamic_reg_values( key ) )
                return False;
+
+       if ( key->hook && key->hook->ops && key->hook->ops->store_values )
+               return key->hook->ops->store_values( key->name, val );
+
+       return False;
 }
 
 
@@ -450,8 +177,8 @@ int fetch_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkey_ctr )
 {
        int result = -1;
        
-       if ( key->hook && key->hook->ops && key->hook->ops->subkey_fn )
-               result = key->hook->ops->subkey_fn( key->name, subkey_ctr );
+       if ( key->hook && key->hook->ops && key->hook->ops->fetch_subkeys )
+               result = key->hook->ops->fetch_subkeys( key->name, subkey_ctr );
 
        return result;
 }
@@ -463,62 +190,75 @@ int fetch_reg_keys( REGISTRY_KEY *key, REGSUBKEY_CTR *subkey_ctr )
 
 BOOL fetch_reg_keys_specific( REGISTRY_KEY *key, char** subkey, uint32 key_index )
 {
-       static REGSUBKEY_CTR ctr;
+       static REGSUBKEY_CTR *ctr = NULL;
        static pstring save_path;
-       static BOOL ctr_init = False;
        char *s;
        
        *subkey = NULL;
        
        /* simple caching for performance; very basic heuristic */
+
+       DEBUG(8,("fetch_reg_keys_specific: Looking for key [%d] of  [%s]\n", key_index, key->name));
        
-       if ( !ctr_init ) {
+       if ( !ctr ) {
                DEBUG(8,("fetch_reg_keys_specific: Initializing cache of subkeys for [%s]\n", key->name));
-               ZERO_STRUCTP( &ctr );   
-               regsubkey_ctr_init( &ctr );
+
+               if ( !(ctr = TALLOC_ZERO_P( NULL, REGSUBKEY_CTR )) ) {
+                       DEBUG(0,("fetch_reg_keys_specific: talloc() failed!\n"));
+                       return False;
+               }
                
                pstrcpy( save_path, key->name );
                
-               if ( fetch_reg_keys( key, &ctr) == -1 )
+               if ( fetch_reg_keys( key, ctr) == -1 )
                        return False;
                        
-               ctr_init = True;
        }
        /* clear the cache when key_index == 0 or the path has changed */
        else if ( !key_index || StrCaseCmp( save_path, key->name) ) {
 
                DEBUG(8,("fetch_reg_keys_specific: Updating cache of subkeys for [%s]\n", key->name));
                
-               regsubkey_ctr_destroy( &ctr );  
-               regsubkey_ctr_init( &ctr );
+               TALLOC_FREE( ctr );
+
+               if ( !(ctr = TALLOC_ZERO_P( NULL, REGSUBKEY_CTR )) ) {
+                       DEBUG(0,("fetch_reg_keys_specific: talloc() failed!\n"));
+                       return False;
+               }
                
                pstrcpy( save_path, key->name );
                
-               if ( fetch_reg_keys( key, &ctr) == -1 )
+               if ( fetch_reg_keys( key, ctr) == -1 )
                        return False;
        }
        
-       if ( !(s = regsubkey_ctr_specific_key( &ctr, key_index )) )
+       if ( !(s = regsubkey_ctr_specific_key( ctr, key_index )) )
                return False;
 
-       *subkey = strdup( s );
+       *subkey = SMB_STRDUP( s );
 
        return True;
 }
 
-
 /***********************************************************************
  High level wrapper function for enumerating registry values
- Initialize the TALLOC_CTX if necessary
  ***********************************************************************/
 
 int fetch_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
 {
        int result = -1;
        
-       if ( key->hook && key->hook->ops && key->hook->ops->value_fn )
-               result = key->hook->ops->value_fn( key->name, val );
+       if ( key->hook && key->hook->ops && key->hook->ops->fetch_values )
+               result = key->hook->ops->fetch_values( key->name, val );
+       
+       /* if the backend lookup returned no data, try the dynamic overlay */
+       
+       if ( result == 0 ) {
+               result = fetch_dynamic_reg_values( key, val );
 
+               return ( result != -1 ) ? result : 0;
+       }
+       
        return result;
 }
 
@@ -530,43 +270,46 @@ int fetch_reg_values( REGISTRY_KEY *key, REGVAL_CTR *val )
 
 BOOL fetch_reg_values_specific( REGISTRY_KEY *key, REGISTRY_VALUE **val, uint32 val_index )
 {
-       static REGVAL_CTR       ctr;
+       static REGVAL_CTR       *ctr = NULL;
        static pstring          save_path;
-       static BOOL             ctr_init = False;
        REGISTRY_VALUE          *v;
        
        *val = NULL;
        
        /* simple caching for performance; very basic heuristic */
        
-       if ( !ctr_init ) {
+       if ( !ctr ) {
                DEBUG(8,("fetch_reg_values_specific: Initializing cache of values for [%s]\n", key->name));
 
-               ZERO_STRUCTP( &ctr );   
-               regval_ctr_init( &ctr );
-               
+               if ( !(ctr = TALLOC_ZERO_P( NULL, REGVAL_CTR )) ) {
+                       DEBUG(0,("fetch_reg_values_specific: talloc() failed!\n"));
+                       return False;
+               }
+
                pstrcpy( save_path, key->name );
                
-               if ( fetch_reg_values( key, &ctr) == -1 )
+               if ( fetch_reg_values( key, ctr) == -1 )
                        return False;
-                       
-               ctr_init = True;
        }
        /* clear the cache when val_index == 0 or the path has changed */
-       else if ( !val_index || StrCaseCmp(save_path, key->name) ) {
+       else if ( !val_index || !strequal(save_path, key->name) ) {
 
                DEBUG(8,("fetch_reg_values_specific: Updating cache of values for [%s]\n", key->name));         
                
-               regval_ctr_destroy( &ctr );     
-               regval_ctr_init( &ctr );
-               
+               TALLOC_FREE( ctr );
+
+               if ( !(ctr = TALLOC_ZERO_P( NULL, REGVAL_CTR )) ) {
+                       DEBUG(0,("fetch_reg_values_specific: talloc() failed!\n"));
+                       return False;
+               }
+
                pstrcpy( save_path, key->name );
                
-               if ( fetch_reg_values( key, &ctr) == -1 )
+               if ( fetch_reg_values( key, ctr) == -1 )
                        return False;
        }
        
-       if ( !(v = regval_ctr_specific_value( &ctr, val_index )) )
+       if ( !(v = regval_ctr_specific_value( ctr, val_index )) )
                return False;
 
        *val = dup_registry_value( v );
@@ -575,33 +318,93 @@ BOOL fetch_reg_values_specific( REGISTRY_KEY *key, REGISTRY_VALUE **val, uint32
 }
 
 /***********************************************************************
- Utility function for splitting the base path of a registry path off
- by setting base and new_path to the apprapriate offsets withing the
- path.
- WARNING!!  Does modify the original string!
+ High level access check for passing the required access mask to the 
+ underlying registry backend
  ***********************************************************************/
 
-BOOL reg_split_path( char *path, char **base, char **new_path )
+BOOL regkey_access_check( REGISTRY_KEY *key, uint32 requested, uint32 *granted, NT_USER_TOKEN *token )
 {
-       char *p;
+       /* use the default security check if the backend has not defined its own */
        
-       *new_path = *base = NULL;
+       if ( !(key->hook && key->hook->ops && key->hook->ops->reg_access_check) ) {
+               SEC_DESC *sec_desc;
+               NTSTATUS status;
+               
+               if ( !(sec_desc = construct_registry_sd( get_talloc_ctx() )) )
+                       return False;
+               
+               status = registry_access_check( sec_desc, token, requested, granted );          
+               
+               return NT_STATUS_IS_OK(status);
+       }
        
-       if ( !path)
-               return False;
+       return key->hook->ops->reg_access_check( key->name, requested, granted, token );
+}
+
+/***********************************************************************
+***********************************************************************/
+
+WERROR regkey_open_internal( REGISTRY_KEY **regkey, const char *path, 
+                             NT_USER_TOKEN *token, uint32 access_desired )
+{
+       WERROR          result = WERR_OK;
+       REGISTRY_KEY    *keyinfo;
+       REGSUBKEY_CTR   *subkeys = NULL;
+       uint32 access_granted;
        
-       *base = path;
+       DEBUG(7,("regkey_open_internal: name = [%s]\n", path));
+
+       if ( !(*regkey = TALLOC_ZERO_P(NULL, REGISTRY_KEY)) )
+               return WERR_NOMEM;
+               
+       keyinfo = *regkey;
+               
+       /* initialization */
+       
+       keyinfo->type = REG_KEY_GENERIC;
+       keyinfo->name = talloc_strdup( keyinfo, path );
        
-       p = strchr( path, '\\' );
        
-       if ( p ) {
-               *p = '\0';
-               *new_path = p+1;
+       /* Tag this as a Performance Counter Key */
+
+       if( StrnCaseCmp(path, KEY_HKPD, strlen(KEY_HKPD)) == 0 )
+               keyinfo->type = REG_KEY_HKPD;
+       
+       /* Look up the table of registry I/O operations */
+
+       if ( !(keyinfo->hook = reghook_cache_find( keyinfo->name )) ) {
+               DEBUG(0,("open_registry_key: Failed to assigned a REGISTRY_HOOK to [%s]\n",
+                       keyinfo->name ));
+               result = WERR_BADFILE;
+               goto done;
        }
        
-       return True;
-}
+       /* check if the path really exists; failed is indicated by -1 */
+       /* if the subkey count failed, bail out */
+
+       if ( !(subkeys = TALLOC_ZERO_P( keyinfo, REGSUBKEY_CTR )) ) {
+               result = WERR_NOMEM;
+               goto done;
+       }
+
+       if ( fetch_reg_keys( keyinfo, subkeys ) == -1 )  {
+               result = WERR_BADFILE;
+               goto done;
+       }
+       
+       TALLOC_FREE( subkeys );
 
+       if ( !regkey_access_check( keyinfo, access_desired, &access_granted, token ) ) {
+               result = WERR_ACCESS_DENIED;
+               goto done;
+       }
+       
+       keyinfo->access_granted = access_granted;
 
+done:
+       if ( !W_ERROR_IS_OK(result) ) {
+               TALLOC_FREE( *regkey );
+       }
 
+       return result;
+}