2 Unix SMB/CIFS implementation.
3 Reading registry patch files
5 Copyright (C) Jelmer Vernooij 2004-2007
6 Copyright (C) Wilco Baan Hofman 2006
7 Copyright (C) Matthias Dieter Wallnöfer 2008-2010
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "lib/registry/registry.h"
25 #include "system/filesys.h"
28 _PUBLIC_ WERROR
reg_preg_diff_load(int fd
,
29 struct smb_iconv_convenience
*iconv_convenience
,
30 const struct reg_diff_callbacks
*callbacks
,
33 _PUBLIC_ WERROR
reg_dotreg_diff_load(int fd
,
34 struct smb_iconv_convenience
*iconv_convenience
,
35 const struct reg_diff_callbacks
*callbacks
,
39 * Generate difference between two keys
41 WERROR
reg_generate_diff_key(struct registry_key
*oldkey
,
42 struct registry_key
*newkey
,
44 const struct reg_diff_callbacks
*callbacks
,
48 struct registry_key
*t1
= NULL
, *t2
= NULL
;
51 WERROR error
, error1
, error2
;
52 TALLOC_CTX
*mem_ctx
= talloc_init("writediff");
53 uint32_t old_num_subkeys
, old_num_values
,
54 new_num_subkeys
, new_num_values
;
57 error
= reg_key_get_info(mem_ctx
, oldkey
, NULL
,
58 &old_num_subkeys
, &old_num_values
,
59 NULL
, NULL
, NULL
, NULL
);
60 if (!W_ERROR_IS_OK(error
)) {
61 DEBUG(0, ("Error occurred while getting key info: %s\n",
71 /* Subkeys that were changed or deleted */
72 for (i
= 0; i
< old_num_subkeys
; i
++) {
73 error1
= reg_key_get_subkey_by_index(mem_ctx
, oldkey
, i
,
74 &keyname1
, NULL
, NULL
);
75 if (!W_ERROR_IS_OK(error1
)) {
76 DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
82 error2
= reg_open_key(mem_ctx
, newkey
, keyname1
, &t2
);
84 error2
= WERR_BADFILE
;
88 if (!W_ERROR_IS_OK(error2
) && !W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
89 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
95 /* if "error2" is going to be "WERR_BADFILE", then newkey */
96 /* didn't have such a subkey and therefore add a del diff */
97 tmppath
= talloc_asprintf(mem_ctx
, "%s\\%s", path
, keyname1
);
98 if (tmppath
== NULL
) {
99 DEBUG(0, ("Out of memory\n"));
100 talloc_free(mem_ctx
);
103 if (!W_ERROR_IS_OK(error2
))
104 callbacks
->del_key(callback_data
, tmppath
);
106 /* perform here also the recursive invocation */
107 error1
= reg_open_key(mem_ctx
, oldkey
, keyname1
, &t1
);
108 if (!W_ERROR_IS_OK(error1
)) {
109 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
110 win_errstr(error1
)));
111 talloc_free(mem_ctx
);
114 reg_generate_diff_key(t1
, t2
, tmppath
, callbacks
, callback_data
);
116 talloc_free(tmppath
);
119 if (newkey
!= NULL
) {
120 error
= reg_key_get_info(mem_ctx
, newkey
, NULL
,
121 &new_num_subkeys
, &new_num_values
,
122 NULL
, NULL
, NULL
, NULL
);
123 if (!W_ERROR_IS_OK(error
)) {
124 DEBUG(0, ("Error occurred while getting key info: %s\n",
126 talloc_free(mem_ctx
);
134 /* Subkeys that were added */
135 for(i
= 0; i
< new_num_subkeys
; i
++) {
136 error1
= reg_key_get_subkey_by_index(mem_ctx
, newkey
, i
,
137 &keyname1
, NULL
, NULL
);
138 if (!W_ERROR_IS_OK(error1
)) {
139 DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
140 win_errstr(error1
)));
141 talloc_free(mem_ctx
);
145 if (oldkey
!= NULL
) {
146 error2
= reg_open_key(mem_ctx
, oldkey
, keyname1
, &t1
);
148 if (W_ERROR_IS_OK(error2
))
151 error2
= WERR_BADFILE
;
155 if (!W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
156 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
157 win_errstr(error2
)));
158 talloc_free(mem_ctx
);
162 /* oldkey didn't have such a subkey, add a add diff */
163 tmppath
= talloc_asprintf(mem_ctx
, "%s\\%s", path
, keyname1
);
164 if (tmppath
== NULL
) {
165 DEBUG(0, ("Out of memory\n"));
166 talloc_free(mem_ctx
);
169 callbacks
->add_key(callback_data
, tmppath
);
171 /* perform here also the recursive invocation */
172 error1
= reg_open_key(mem_ctx
, newkey
, keyname1
, &t2
);
173 if (!W_ERROR_IS_OK(error1
)) {
174 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
175 win_errstr(error1
)));
176 talloc_free(mem_ctx
);
179 reg_generate_diff_key(t1
, t2
, tmppath
, callbacks
, callback_data
);
181 talloc_free(tmppath
);
184 /* Values that were added or changed */
185 for(i
= 0; i
< new_num_values
; i
++) {
187 uint32_t type1
, type2
;
188 DATA_BLOB contents1
= { NULL
, 0 }, contents2
= { NULL
, 0 };
190 error1
= reg_key_get_value_by_index(mem_ctx
, newkey
, i
,
191 &name
, &type1
, &contents1
);
192 if (!W_ERROR_IS_OK(error1
)) {
193 DEBUG(0, ("Unable to get value by index: %s\n",
194 win_errstr(error1
)));
195 talloc_free(mem_ctx
);
199 if (oldkey
!= NULL
) {
200 error2
= reg_key_get_value_by_name(mem_ctx
, oldkey
,
204 error2
= WERR_BADFILE
;
206 if (!W_ERROR_IS_OK(error2
)
207 && !W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
208 DEBUG(0, ("Error occurred while getting value by name: %s\n",
209 win_errstr(error2
)));
210 talloc_free(mem_ctx
);
214 if (W_ERROR_IS_OK(error2
)
215 && (data_blob_cmp(&contents1
, &contents2
) == 0)
216 && (type1
== type2
)) {
217 talloc_free(discard_const_p(char, name
));
218 talloc_free(contents1
.data
);
219 talloc_free(contents2
.data
);
223 callbacks
->set_value(callback_data
, path
, name
,
226 talloc_free(discard_const_p(char, name
));
227 talloc_free(contents1
.data
);
228 talloc_free(contents2
.data
);
231 /* Values that were deleted */
232 for (i
= 0; i
< old_num_values
; i
++) {
235 DATA_BLOB contents
= { NULL
, 0 };
237 error1
= reg_key_get_value_by_index(mem_ctx
, oldkey
, i
, &name
,
239 if (!W_ERROR_IS_OK(error1
)) {
240 DEBUG(0, ("Unable to get value by index: %s\n",
241 win_errstr(error1
)));
242 talloc_free(mem_ctx
);
247 error2
= reg_key_get_value_by_name(mem_ctx
, newkey
,
248 name
, &type
, &contents
);
250 error2
= WERR_BADFILE
;
252 if (W_ERROR_IS_OK(error2
)) {
253 talloc_free(discard_const_p(char, name
));
254 talloc_free(contents
.data
);
258 if (!W_ERROR_EQUAL(error2
, WERR_BADFILE
)) {
259 DEBUG(0, ("Error occurred while getting value by name: %s\n",
260 win_errstr(error2
)));
261 talloc_free(mem_ctx
);
265 callbacks
->del_value(callback_data
, path
, name
);
267 talloc_free(discard_const_p(char, name
));
268 talloc_free(contents
.data
);
271 talloc_free(mem_ctx
);
276 * Generate diff between two registry contexts
278 _PUBLIC_ WERROR
reg_generate_diff(struct registry_context
*ctx1
,
279 struct registry_context
*ctx2
,
280 const struct reg_diff_callbacks
*callbacks
,
286 for (i
= 0; reg_predefined_keys
[i
].name
; i
++) {
287 struct registry_key
*r1
= NULL
, *r2
= NULL
;
289 error
= reg_get_predefined_key(ctx1
,
290 reg_predefined_keys
[i
].handle
, &r1
);
291 if (!W_ERROR_IS_OK(error
) &&
292 !W_ERROR_EQUAL(error
, WERR_BADFILE
)) {
293 DEBUG(0, ("Unable to open hive %s for backend 1\n",
294 reg_predefined_keys
[i
].name
));
298 error
= reg_get_predefined_key(ctx2
,
299 reg_predefined_keys
[i
].handle
, &r2
);
300 if (!W_ERROR_IS_OK(error
) &&
301 !W_ERROR_EQUAL(error
, WERR_BADFILE
)) {
302 DEBUG(0, ("Unable to open hive %s for backend 2\n",
303 reg_predefined_keys
[i
].name
));
307 /* if "r1" is NULL (old hive) and "r2" isn't (new hive) then
308 * the hive doesn't exist yet and we have to generate an add
310 if ((r1
== NULL
) && (r2
!= NULL
)) {
311 callbacks
->add_key(callback_data
,
312 reg_predefined_keys
[i
].name
);
314 /* if "r1" isn't NULL (old hive) and "r2" is (new hive) then
315 * the hive shouldn't exist anymore and we have to generate a
317 if ((r1
!= NULL
) && (r2
== NULL
)) {
318 callbacks
->del_key(callback_data
,
319 reg_predefined_keys
[i
].name
);
322 error
= reg_generate_diff_key(r1
, r2
,
323 reg_predefined_keys
[i
].name
, callbacks
,
325 if (!W_ERROR_IS_OK(error
)) {
326 DEBUG(0, ("Unable to determine diff: %s\n",
331 if (callbacks
->done
!= NULL
) {
332 callbacks
->done(callback_data
);
340 _PUBLIC_ WERROR
reg_diff_load(const char *filename
,
341 struct smb_iconv_convenience
*iconv_convenience
,
342 const struct reg_diff_callbacks
*callbacks
,
348 fd
= open(filename
, O_RDONLY
, 0);
350 DEBUG(0, ("Error opening registry patch file `%s'\n",
352 return WERR_GENERAL_FAILURE
;
355 if (read(fd
, &hdr
, 4) != 4) {
356 DEBUG(0, ("Error reading registry patch file `%s'\n",
359 return WERR_GENERAL_FAILURE
;
362 /* Reset position in file */
363 lseek(fd
, 0, SEEK_SET
);
364 #if 0 /* These backends are not supported yet. */
365 if (strncmp(hdr
, "CREG", 4) == 0) {
366 /* Must be a W9x CREG Config.pol file */
367 return reg_creg_diff_load(diff
, fd
);
368 } else if (strncmp(hdr
, "regf", 4) == 0) {
369 /* Must be a REGF NTConfig.pol file */
370 return reg_regf_diff_load(diff
, fd
);
373 if (strncmp(hdr
, "PReg", 4) == 0) {
374 /* Must be a GPO Registry.pol file */
375 return reg_preg_diff_load(fd
, iconv_convenience
, callbacks
, callback_data
);
377 /* Must be a normal .REG file */
378 return reg_dotreg_diff_load(fd
, iconv_convenience
, callbacks
, callback_data
);
383 * The reg_diff_apply functions
385 static WERROR
reg_diff_apply_add_key(void *_ctx
, const char *key_name
)
387 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
388 struct registry_key
*tmp
;
392 /* Recursively create the path */
393 buf
= talloc_strdup(ctx
, key_name
);
394 W_ERROR_HAVE_NO_MEMORY(buf
);
397 while (*buf_ptr
++ != '\0' ) {
398 if (*buf_ptr
== '\\') {
400 error
= reg_key_add_abs(ctx
, ctx
, buf
, 0, NULL
, &tmp
);
402 if (!W_ERROR_EQUAL(error
, WERR_ALREADY_EXISTS
) &&
403 !W_ERROR_IS_OK(error
)) {
404 DEBUG(0, ("Error adding new key '%s': %s\n",
405 key_name
, win_errstr(error
)));
416 error
= reg_key_add_abs(ctx
, ctx
, key_name
, 0, NULL
, &tmp
);
418 if (!W_ERROR_EQUAL(error
, WERR_ALREADY_EXISTS
) &&
419 !W_ERROR_IS_OK(error
)) {
420 DEBUG(0, ("Error adding new key '%s': %s\n",
421 key_name
, win_errstr(error
)));
429 static WERROR
reg_diff_apply_del_key(void *_ctx
, const char *key_name
)
431 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
433 /* We can't proof here for success, because a common superkey could */
434 /* have been deleted before the subkey's (diff order). This removed */
435 /* therefore all children recursively and the "WERR_BADFILE" result is */
438 reg_key_del_abs(ctx
, key_name
);
443 static WERROR
reg_diff_apply_set_value(void *_ctx
, const char *path
,
444 const char *value_name
,
445 uint32_t value_type
, DATA_BLOB value
)
447 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
448 struct registry_key
*tmp
;
452 error
= reg_open_key_abs(ctx
, ctx
, path
, &tmp
);
454 if (W_ERROR_EQUAL(error
, WERR_BADFILE
)) {
455 DEBUG(0, ("Error opening key '%s'\n", path
));
460 error
= reg_val_set(tmp
, value_name
,
462 if (!W_ERROR_IS_OK(error
)) {
463 DEBUG(0, ("Error setting value '%s'\n", value_name
));
472 static WERROR
reg_diff_apply_del_value(void *_ctx
, const char *key_name
,
473 const char *value_name
)
475 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
476 struct registry_key
*tmp
;
480 error
= reg_open_key_abs(ctx
, ctx
, key_name
, &tmp
);
482 if (!W_ERROR_IS_OK(error
)) {
483 DEBUG(0, ("Error opening key '%s'\n", key_name
));
487 error
= reg_del_value(ctx
, tmp
, value_name
);
488 if (!W_ERROR_IS_OK(error
)) {
489 DEBUG(0, ("Error deleting value '%s'\n", value_name
));
498 static WERROR
reg_diff_apply_del_all_values(void *_ctx
, const char *key_name
)
500 struct registry_context
*ctx
= (struct registry_context
*)_ctx
;
501 struct registry_key
*key
;
503 const char *value_name
;
505 error
= reg_open_key_abs(ctx
, ctx
, key_name
, &key
);
507 if (!W_ERROR_IS_OK(error
)) {
508 DEBUG(0, ("Error opening key '%s'\n", key_name
));
512 W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx
, key
, NULL
,
513 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
));
515 while (W_ERROR_IS_OK(reg_key_get_value_by_index(
516 ctx
, key
, 0, &value_name
, NULL
, NULL
))) {
517 error
= reg_del_value(ctx
, key
, value_name
);
518 if (!W_ERROR_IS_OK(error
)) {
519 DEBUG(0, ("Error deleting value '%s'\n", value_name
));
522 talloc_free(discard_const_p(char, value_name
));
531 * Apply diff to a registry context
533 _PUBLIC_ WERROR
reg_diff_apply(struct registry_context
*ctx
,
534 struct smb_iconv_convenience
*iconv_convenience
,
535 const char *filename
)
537 struct reg_diff_callbacks callbacks
;
539 callbacks
.add_key
= reg_diff_apply_add_key
;
540 callbacks
.del_key
= reg_diff_apply_del_key
;
541 callbacks
.set_value
= reg_diff_apply_set_value
;
542 callbacks
.del_value
= reg_diff_apply_del_value
;
543 callbacks
.del_all_values
= reg_diff_apply_del_all_values
;
544 callbacks
.done
= NULL
;
546 return reg_diff_load(filename
, iconv_convenience
,