2005-09-29 16:00:49 +04:00
/*
Unix SMB / CIFS implementation .
2007-08-26 19:16:40 +04:00
Reading registry patch files
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
Copyright ( C ) Jelmer Vernooij 2004 - 2007
Copyright ( C ) Wilco Baan Hofman 2006
2005-09-29 16:00:49 +04:00
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
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2005-09-29 16:00:49 +04:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-09-29 16:00:49 +04:00
*/
# include "includes.h"
2007-08-26 19:16:40 +04:00
# include "lib/registry/patchfile.h"
2005-09-29 16:00:49 +04:00
# include "lib/registry/registry.h"
# include "system/filesys.h"
2007-08-26 19:16:40 +04:00
_PUBLIC_ WERROR reg_preg_diff_load ( int fd , const struct reg_diff_callbacks * callbacks , void * callback_data ) ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
_PUBLIC_ WERROR reg_dotreg_diff_load ( int fd , const struct reg_diff_callbacks * callbacks , void * callback_data ) ;
2005-09-29 16:00:49 +04:00
/*
* Generate difference between two keys
*/
2007-08-26 19:16:40 +04:00
WERROR reg_generate_diff_key ( struct registry_key * oldkey ,
struct registry_key * newkey ,
const char * path ,
const struct reg_diff_callbacks * callbacks ,
void * callback_data )
2005-09-29 16:00:49 +04:00
{
int i ;
struct registry_key * t1 , * t2 ;
2007-08-26 19:16:40 +04:00
char * tmppath ;
const char * keyname1 ;
WERROR error , error1 , error2 ;
2005-09-29 16:00:49 +04:00
TALLOC_CTX * mem_ctx = talloc_init ( " writediff " ) ;
2007-08-26 19:16:40 +04:00
uint32_t old_num_subkeys , old_num_values ,
new_num_subkeys , new_num_values ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
if ( oldkey ! = NULL ) {
error = reg_key_get_info ( mem_ctx , oldkey , NULL , & old_num_subkeys , & old_num_values ,
NULL ) ;
if ( ! W_ERROR_IS_OK ( error ) ) {
DEBUG ( 0 , ( " Error occured while getting key info: %s \n " ,
win_errstr ( error ) ) ) ;
return error ;
}
} else {
old_num_subkeys = 0 ;
old_num_values = 0 ;
}
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
/* Subkeys that were deleted */
for ( i = 0 ; i < old_num_subkeys ; i + + ) {
error1 = reg_key_get_subkey_by_index ( mem_ctx , oldkey , i , & keyname1 ,
NULL , NULL ) ;
if ( ! W_ERROR_IS_OK ( error1 ) ) {
DEBUG ( 0 , ( " Error occured while getting subkey by index: %s \n " ,
win_errstr ( error2 ) ) ) ;
2005-09-29 16:00:49 +04:00
continue ;
2007-08-26 19:16:40 +04:00
}
if ( newkey ! = NULL ) {
error2 = reg_open_key ( mem_ctx , newkey , keyname1 , & t2 ) ;
if ( W_ERROR_IS_OK ( error2 ) )
continue ;
} else {
error2 = WERR_DEST_NOT_FOUND ;
t2 = NULL ;
}
2005-09-29 16:00:49 +04:00
if ( ! W_ERROR_EQUAL ( error2 , WERR_DEST_NOT_FOUND ) ) {
2007-08-26 19:16:40 +04:00
DEBUG ( 0 , ( " Error occured while getting subkey by name: %s \n " ,
win_errstr ( error2 ) ) ) ;
talloc_free ( mem_ctx ) ;
2005-09-29 16:00:49 +04:00
return error2 ;
}
/* newkey didn't have such a subkey, add del diff */
2007-08-26 19:16:40 +04:00
tmppath = talloc_asprintf ( mem_ctx , " %s \\ %s " , path , keyname1 ) ;
callbacks - > del_key ( callback_data , tmppath ) ;
talloc_free ( tmppath ) ;
2005-09-29 16:00:49 +04:00
}
2007-08-26 19:16:40 +04:00
if ( newkey ! = NULL ) {
error = reg_key_get_info ( mem_ctx , newkey , NULL , & new_num_subkeys , & new_num_values ,
NULL ) ;
if ( ! W_ERROR_IS_OK ( error ) ) {
DEBUG ( 0 , ( " Error occured while getting key info: %s \n " ,
win_errstr ( error ) ) ) ;
return error ;
}
} else {
new_num_subkeys = 0 ;
new_num_values = 0 ;
2005-09-29 16:00:49 +04:00
}
/* Subkeys that were added */
2007-08-26 19:16:40 +04:00
for ( i = 0 ; i < new_num_subkeys ; i + + ) {
error1 = reg_key_get_subkey_by_index ( mem_ctx , newkey , i , & keyname1 ,
NULL , NULL ) ;
if ( ! W_ERROR_IS_OK ( error1 ) ) {
DEBUG ( 0 , ( " Error occured while getting subkey by index: %s \n " ,
win_errstr ( error1 ) ) ) ;
talloc_free ( mem_ctx ) ;
return error1 ;
}
if ( oldkey ! = NULL ) {
error2 = reg_open_key ( mem_ctx , oldkey , keyname1 , & t1 ) ;
if ( W_ERROR_IS_OK ( error2 ) )
continue ;
} else {
t1 = NULL ;
error2 = WERR_DEST_NOT_FOUND ;
}
2005-09-29 16:00:49 +04:00
if ( ! W_ERROR_EQUAL ( error2 , WERR_DEST_NOT_FOUND ) ) {
2007-08-26 19:16:40 +04:00
DEBUG ( 0 , ( " Error occured while getting subkey by name: %s \n " ,
win_errstr ( error2 ) ) ) ;
talloc_free ( mem_ctx ) ;
2005-09-29 16:00:49 +04:00
return error2 ;
}
/* oldkey didn't have such a subkey, add add diff */
2007-08-26 19:16:40 +04:00
tmppath = talloc_asprintf ( mem_ctx , " %s \\ %s " , path , keyname1 ) ;
callbacks - > add_key ( callback_data , tmppath ) ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
W_ERROR_NOT_OK_RETURN (
reg_open_key ( mem_ctx , newkey , keyname1 , & t2 ) ) ;
reg_generate_diff_key ( t1 , t2 , tmppath , callbacks , callback_data ) ;
talloc_free ( tmppath ) ;
2005-09-29 16:00:49 +04:00
}
/* Values that were changed */
2007-08-26 19:16:40 +04:00
for ( i = 0 ; i < new_num_values ; i + + ) {
const char * name ;
uint32_t type1 , type2 ;
DATA_BLOB contents1 , contents2 ;
error1 = reg_key_get_value_by_index ( mem_ctx , newkey , i ,
& name , & type1 , & contents1 ) ;
if ( ! W_ERROR_IS_OK ( error1 ) ) {
DEBUG ( 0 , ( " Unable to get key by index: %s \n " ,
win_errstr ( error1 ) ) ) ;
talloc_free ( mem_ctx ) ;
return error1 ;
}
if ( oldkey ! = NULL ) {
error2 = reg_key_get_value_by_name ( mem_ctx , oldkey , name ,
& type2 , & contents2 ) ;
} else
error2 = WERR_DEST_NOT_FOUND ;
2005-09-29 16:00:49 +04:00
if ( ! W_ERROR_IS_OK ( error2 ) & &
! W_ERROR_EQUAL ( error2 , WERR_DEST_NOT_FOUND ) ) {
2007-08-26 19:16:40 +04:00
DEBUG ( 0 , ( " Error occured while getting value by name: %s \n " ,
win_errstr ( error2 ) ) ) ;
talloc_free ( mem_ctx ) ;
2005-09-29 16:00:49 +04:00
return error2 ;
}
2007-08-26 19:16:40 +04:00
if ( W_ERROR_IS_OK ( error2 ) & & data_blob_cmp ( & contents1 , & contents2 ) = = 0 )
2005-09-29 16:00:49 +04:00
continue ;
2007-08-26 19:16:40 +04:00
callbacks - > set_value ( callback_data , path , name , type1 , contents1 ) ;
2005-09-29 16:00:49 +04:00
}
/* Values that were deleted */
2007-08-26 19:16:40 +04:00
for ( i = 0 ; i < old_num_values ; i + + ) {
const char * name ;
error1 = reg_key_get_value_by_index ( mem_ctx , oldkey , i , & name ,
NULL , NULL ) ;
if ( ! W_ERROR_IS_OK ( error1 ) ) {
DEBUG ( 0 , ( " Error ocurred getting value by index: %s \n " ,
win_errstr ( error1 ) ) ) ;
talloc_free ( mem_ctx ) ;
return error1 ;
}
error2 = reg_key_get_value_by_name ( mem_ctx , newkey , name , NULL ,
NULL ) ;
2005-09-29 16:00:49 +04:00
if ( W_ERROR_IS_OK ( error2 ) )
continue ;
if ( ! W_ERROR_EQUAL ( error2 , WERR_DEST_NOT_FOUND ) ) {
2007-08-26 19:16:40 +04:00
DEBUG ( 0 , ( " Error occured while getting value by name: %s \n " ,
win_errstr ( error2 ) ) ) ;
2005-09-29 16:00:49 +04:00
return error2 ;
}
2007-08-26 19:16:40 +04:00
callbacks - > del_value ( callback_data , path , name ) ;
2005-09-29 16:00:49 +04:00
}
talloc_free ( mem_ctx ) ;
return WERR_OK ;
}
2006-03-06 13:09:53 +03:00
/**
2005-09-29 16:00:49 +04:00
* Generate diff between two registry contexts
*/
2007-08-26 19:16:40 +04:00
_PUBLIC_ WERROR reg_generate_diff ( struct registry_context * ctx1 ,
struct registry_context * ctx2 ,
const struct reg_diff_callbacks * callbacks ,
void * callback_data )
2005-09-29 16:00:49 +04:00
{
int i ;
WERROR error ;
2007-08-26 19:16:40 +04:00
for ( i = HKEY_FIRST ; i < = HKEY_LAST ; i + + ) {
struct registry_key * r1 = NULL , * r2 = NULL ;
2005-09-29 16:00:49 +04:00
error = reg_get_predefined_key ( ctx1 , i , & r1 ) ;
2007-08-26 19:16:40 +04:00
if ( ! W_ERROR_IS_OK ( error ) & & ! W_ERROR_EQUAL ( error , WERR_NOT_FOUND ) ) {
2005-09-29 16:00:49 +04:00
DEBUG ( 0 , ( " Unable to open hive %s for backend 1 \n " , reg_get_predef_name ( i ) ) ) ;
}
error = reg_get_predefined_key ( ctx2 , i , & r2 ) ;
2007-08-26 19:16:40 +04:00
if ( ! W_ERROR_IS_OK ( error ) & & ! W_ERROR_EQUAL ( error , WERR_NOT_FOUND ) ) {
2005-09-29 16:00:49 +04:00
DEBUG ( 0 , ( " Unable to open hive %s for backend 2 \n " , reg_get_predef_name ( i ) ) ) ;
}
2007-08-26 19:16:40 +04:00
if ( r1 = = NULL & & r2 = = NULL )
2005-09-29 16:00:49 +04:00
continue ;
2007-08-26 19:16:40 +04:00
error = reg_generate_diff_key ( r1 , r2 , reg_get_predef_name ( i ) , callbacks , callback_data ) ;
if ( ! W_ERROR_IS_OK ( error ) ) {
DEBUG ( 0 , ( " Unable to determine diff: %s \n " , win_errstr ( error ) ) ) ;
return error ;
2005-09-29 16:00:49 +04:00
}
}
2007-08-26 19:16:40 +04:00
if ( callbacks - > done ! = NULL ) {
callbacks - > done ( callback_data ) ;
}
2005-09-29 16:00:49 +04:00
return WERR_OK ;
}
2006-03-06 13:09:53 +03:00
/**
2005-09-29 16:00:49 +04:00
* Load diff file
*/
2007-08-27 17:13:08 +04:00
_PUBLIC_ WERROR reg_diff_load ( const char * filename ,
2007-09-07 17:31:15 +04:00
const struct reg_diff_callbacks * callbacks ,
void * callback_data )
2005-09-29 16:00:49 +04:00
{
int fd ;
2007-08-26 19:16:40 +04:00
char hdr [ 4 ] ;
fd = open ( filename , O_RDONLY , 0 ) ;
2005-09-29 16:00:49 +04:00
if ( fd = = - 1 ) {
2007-08-26 19:16:40 +04:00
DEBUG ( 0 , ( " Error opening registry patch file `%s' \n " , filename ) ) ;
return WERR_GENERAL_FAILURE ;
2005-09-29 16:00:49 +04:00
}
2007-08-26 19:16:40 +04:00
if ( read ( fd , & hdr , 4 ) ! = 4 ) {
DEBUG ( 0 , ( " Error reading registry patch file `%s' \n " , filename ) ) ;
return WERR_GENERAL_FAILURE ;
2005-09-29 16:00:49 +04:00
}
2007-08-26 19:16:40 +04:00
/* Reset position in file */
lseek ( fd , 0 , SEEK_SET ) ;
#if 0
if ( strncmp ( hdr , " CREG " , 4 ) = = 0 ) {
/* Must be a W9x CREG Config.pol file */
return reg_creg_diff_load ( diff , fd ) ;
} else if ( strncmp ( hdr , " regf " , 4 ) = = 0 ) {
/* Must be a REGF NTConfig.pol file */
return reg_regf_diff_load ( diff , fd ) ;
} else
# endif
if ( strncmp ( hdr , " PReg " , 4 ) = = 0 ) {
/* Must be a GPO Registry.pol file */
return reg_preg_diff_load ( fd , callbacks , callback_data ) ;
} else {
/* Must be a normal .REG file */
return reg_dotreg_diff_load ( fd , callbacks , callback_data ) ;
2005-09-29 16:00:49 +04:00
}
2007-08-26 19:16:40 +04:00
}
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
/**
* The reg_diff_apply functions
*/
static WERROR reg_diff_apply_add_key ( void * _ctx , const char * key_name )
{
2007-09-07 17:31:15 +04:00
struct registry_context * ctx = ( struct registry_context * ) _ctx ;
2007-08-26 19:16:40 +04:00
struct registry_key * tmp ;
WERROR error ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
error = reg_key_add_abs ( ctx , ctx , key_name , 0 , NULL , & tmp ) ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
if ( ! W_ERROR_EQUAL ( error , WERR_ALREADY_EXISTS ) & & ! W_ERROR_IS_OK ( error ) ) {
DEBUG ( 0 , ( " Error adding new key '%s': %s \n " , key_name , win_errstr ( error ) ) ) ;
return error ;
}
return WERR_OK ;
}
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
static WERROR reg_diff_apply_del_key ( void * _ctx , const char * key_name )
{
2007-09-07 17:31:15 +04:00
struct registry_context * ctx = ( struct registry_context * ) _ctx ;
2007-08-26 19:16:40 +04:00
WERROR error ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
error = reg_key_del_abs ( ctx , key_name ) ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
if ( ! W_ERROR_IS_OK ( error ) ) {
DEBUG ( 0 , ( " Unable to delete key '%s' \n " , key_name ) ) ;
return error ;
}
return WERR_OK ;
}
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
static WERROR reg_diff_apply_set_value ( void * _ctx , const char * path , const char * value_name , uint32_t value_type , DATA_BLOB value )
{
2007-09-07 17:31:15 +04:00
struct registry_context * ctx = ( struct registry_context * ) _ctx ;
2007-08-26 19:16:40 +04:00
struct registry_key * tmp ;
WERROR error ;
/* Open key */
error = reg_open_key_abs ( ctx , ctx , path , & tmp ) ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
if ( W_ERROR_EQUAL ( error , WERR_DEST_NOT_FOUND ) ) {
DEBUG ( 0 , ( " Error opening key '%s' \n " , path ) ) ;
return error ;
}
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
/* Set value */
error = reg_val_set ( tmp , value_name ,
value_type , value ) ;
if ( ! W_ERROR_IS_OK ( error ) ) {
DEBUG ( 0 , ( " Error setting value '%s' \n " , value_name ) ) ;
return error ;
}
return WERR_OK ;
}
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
static WERROR reg_diff_apply_del_value ( void * _ctx , const char * key_name , const char * value_name )
{
2007-09-07 17:31:15 +04:00
struct registry_context * ctx = ( struct registry_context * ) _ctx ;
2007-08-26 19:16:40 +04:00
struct registry_key * tmp ;
WERROR error ;
/* Open key */
error = reg_open_key_abs ( ctx , ctx , key_name , & tmp ) ;
if ( ! W_ERROR_IS_OK ( error ) ) {
DEBUG ( 0 , ( " Error opening key '%s' \n " , key_name ) ) ;
return error ;
2005-09-29 16:00:49 +04:00
}
2007-08-26 19:16:40 +04:00
error = reg_del_value ( tmp , value_name ) ;
if ( ! W_ERROR_IS_OK ( error ) ) {
DEBUG ( 0 , ( " Error deleting value '%s' \n " , value_name ) ) ;
return error ;
}
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
return WERR_OK ;
2005-09-29 16:00:49 +04:00
}
2007-08-26 19:16:40 +04:00
static WERROR reg_diff_apply_del_all_values ( void * _ctx , const char * key_name )
2005-09-29 16:00:49 +04:00
{
2007-09-07 17:31:15 +04:00
struct registry_context * ctx = ( struct registry_context * ) _ctx ;
2007-08-26 19:16:40 +04:00
struct registry_key * key ;
2005-09-29 16:00:49 +04:00
WERROR error ;
2007-08-26 19:16:40 +04:00
int i ;
uint32_t num_values ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
error = reg_open_key_abs ( ctx , ctx , key_name , & key ) ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
if ( ! W_ERROR_IS_OK ( error ) ) {
DEBUG ( 0 , ( " Error opening key '%s' \n " , key_name ) ) ;
return error ;
}
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
W_ERROR_NOT_OK_RETURN ( reg_key_get_info ( ctx , key ,
NULL ,
NULL ,
& num_values ,
NULL ) ) ;
for ( i = 0 ; i < num_values ; i + + ) {
const char * name ;
W_ERROR_NOT_OK_RETURN ( reg_key_get_value_by_index ( ctx , key , i , & name ,
NULL , NULL ) ) ;
W_ERROR_NOT_OK_RETURN ( reg_del_value ( key , name ) ) ;
}
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
return WERR_OK ;
}
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
/**
* Apply diff to a registry context
*/
_PUBLIC_ WERROR reg_diff_apply ( const char * filename , struct registry_context * ctx )
{
struct reg_diff_callbacks callbacks ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
callbacks . add_key = reg_diff_apply_add_key ;
callbacks . del_key = reg_diff_apply_del_key ;
callbacks . set_value = reg_diff_apply_set_value ;
callbacks . del_value = reg_diff_apply_del_value ;
callbacks . del_all_values = reg_diff_apply_del_all_values ;
callbacks . done = NULL ;
2005-09-29 16:00:49 +04:00
2007-08-26 19:16:40 +04:00
return reg_diff_load ( filename , & callbacks , ctx ) ;
2005-09-29 16:00:49 +04:00
}