/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- reg_generate_diff_key
- reg_generate_diff
- reg_diff_load
- reg_diff_apply_add_key
- reg_diff_apply_del_key
- reg_diff_apply_set_value
- reg_diff_apply_del_value
- reg_diff_apply_del_all_values
- reg_diff_apply
1 /*
2 Unix SMB/CIFS implementation.
3 Reading registry patch files
4
5 Copyright (C) Jelmer Vernooij 2004-2007
6 Copyright (C) Wilco Baan Hofman 2006
7 Copyright (C) Matthias Dieter Wallnöfer 2008
8
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.
13
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.
18
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/>.
21 */
22
23 #include "includes.h"
24 #include "lib/registry/registry.h"
25 #include "system/filesys.h"
26
27
28 _PUBLIC_ WERROR reg_preg_diff_load(int fd,
29 struct smb_iconv_convenience *iconv_convenience,
30 const struct reg_diff_callbacks *callbacks,
31 void *callback_data);
32
33 _PUBLIC_ WERROR reg_dotreg_diff_load(int fd,
34 struct smb_iconv_convenience *iconv_convenience,
35 const struct reg_diff_callbacks *callbacks,
36 void *callback_data);
37
38 /*
39 * Generate difference between two keys
40 */
41 WERROR reg_generate_diff_key(struct registry_key *oldkey,
/* [<][>][^][v][top][bottom][index][help] */
42 struct registry_key *newkey,
43 const char *path,
44 const struct reg_diff_callbacks *callbacks,
45 void *callback_data)
46 {
47 int i;
48 struct registry_key *t1 = NULL, *t2 = NULL;
49 char *tmppath;
50 const char *keyname1;
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;
55
56 if (oldkey != NULL) {
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",
62 win_errstr(error)));
63 talloc_free(mem_ctx);
64 return error;
65 }
66 } else {
67 old_num_subkeys = 0;
68 old_num_values = 0;
69 }
70
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",
77 win_errstr(error1)));
78 continue;
79 }
80
81 if (newkey != NULL) {
82 error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
83 } else {
84 error2 = WERR_BADFILE;
85 t2 = NULL;
86 }
87
88 if (!W_ERROR_IS_OK(error2) && !W_ERROR_EQUAL(error2, WERR_BADFILE)) {
89 DEBUG(0, ("Error occured while getting subkey by name: %s\n",
90 win_errstr(error2)));
91 talloc_free(mem_ctx);
92 return error2;
93 }
94
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 (!W_ERROR_IS_OK(error2))
99 callbacks->del_key(callback_data, tmppath);
100
101 /* perform here also the recursive invocation */
102 error1 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
103 if (!W_ERROR_IS_OK(error1)) {
104 DEBUG(0, ("Error occured while getting subkey by name: %s\n",
105 win_errstr(error1)));
106 talloc_free(mem_ctx);
107 return error1;
108 }
109 reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
110
111 talloc_free(tmppath);
112 }
113
114 if (newkey != NULL) {
115 error = reg_key_get_info(mem_ctx, newkey, NULL,
116 &new_num_subkeys, &new_num_values,
117 NULL, NULL, NULL, NULL);
118 if (!W_ERROR_IS_OK(error)) {
119 DEBUG(0, ("Error occurred while getting key info: %s\n",
120 win_errstr(error)));
121 talloc_free(mem_ctx);
122 return error;
123 }
124 } else {
125 new_num_subkeys = 0;
126 new_num_values = 0;
127 }
128
129 /* Subkeys that were added */
130 for(i = 0; i < new_num_subkeys; i++) {
131 error1 = reg_key_get_subkey_by_index(mem_ctx, newkey, i,
132 &keyname1, NULL, NULL);
133 if (!W_ERROR_IS_OK(error1)) {
134 DEBUG(0, ("Error occurred while getting subkey by index: %s\n",
135 win_errstr(error1)));
136 talloc_free(mem_ctx);
137 return error1;
138 }
139
140 if (oldkey != NULL) {
141 error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
142
143 if (W_ERROR_IS_OK(error2))
144 continue;
145 } else {
146 error2 = WERR_BADFILE;
147 t1 = NULL;
148 }
149
150 if (!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
151 DEBUG(0, ("Error occurred while getting subkey by name: %s\n",
152 win_errstr(error2)));
153 talloc_free(mem_ctx);
154 return error2;
155 }
156
157 /* oldkey didn't have such a subkey, add add diff */
158 tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
159 callbacks->add_key(callback_data, tmppath);
160
161 /* perform here also the recursive invocation */
162 error1 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
163 if (!W_ERROR_IS_OK(error1)) {
164 DEBUG(0, ("Error occured while getting subkey by name: %s\n",
165 win_errstr(error1)));
166 talloc_free(mem_ctx);
167 return error1;
168 }
169 reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
170
171 talloc_free(tmppath);
172 }
173
174 /* Values that were added or changed */
175 for(i = 0; i < new_num_values; i++) {
176 const char *name;
177 uint32_t type1, type2;
178 DATA_BLOB contents1, contents2;
179
180 error1 = reg_key_get_value_by_index(mem_ctx, newkey, i,
181 &name, &type1, &contents1);
182 if (!W_ERROR_IS_OK(error1)) {
183 DEBUG(0, ("Unable to get value by index: %s\n",
184 win_errstr(error1)));
185 talloc_free(mem_ctx);
186 return error1;
187 }
188
189 if (oldkey != NULL) {
190 error2 = reg_key_get_value_by_name(mem_ctx, oldkey,
191 name, &type2,
192 &contents2);
193 } else
194 error2 = WERR_BADFILE;
195
196 if (!W_ERROR_IS_OK(error2)
197 && !W_ERROR_EQUAL(error2, WERR_BADFILE)) {
198 DEBUG(0, ("Error occurred while getting value by name: %s\n",
199 win_errstr(error2)));
200 talloc_free(mem_ctx);
201 return error2;
202 }
203
204 if (W_ERROR_IS_OK(error2)
205 && (data_blob_cmp(&contents1, &contents2) == 0)
206 && (type1 == type2))
207 continue;
208
209 callbacks->set_value(callback_data, path, name,
210 type1, contents1);
211 }
212
213 /* Values that were deleted */
214 for (i = 0; i < old_num_values; i++) {
215 const char *name;
216 uint32_t type;
217 DATA_BLOB contents;
218
219 error1 = reg_key_get_value_by_index(mem_ctx, oldkey, i, &name,
220 &type, &contents);
221 if (!W_ERROR_IS_OK(error1)) {
222 DEBUG(0, ("Unable to get value by index: %s\n",
223 win_errstr(error1)));
224 talloc_free(mem_ctx);
225 return error1;
226 }
227
228 if (newkey != NULL)
229 error2 = reg_key_get_value_by_name(mem_ctx, newkey,
230 name, &type, &contents);
231 else
232 error2 = WERR_BADFILE;
233
234 if (W_ERROR_IS_OK(error2))
235 continue;
236
237 if (!W_ERROR_EQUAL(error2, WERR_BADFILE)) {
238 DEBUG(0, ("Error occurred while getting value by name: %s\n",
239 win_errstr(error2)));
240 talloc_free(mem_ctx);
241 return error2;
242 }
243
244 callbacks->del_value(callback_data, path, name);
245 }
246
247 talloc_free(mem_ctx);
248 return WERR_OK;
249 }
250
251 /**
252 * Generate diff between two registry contexts
253 */
254 _PUBLIC_ WERROR reg_generate_diff(struct registry_context *ctx1,
/* [<][>][^][v][top][bottom][index][help] */
255 struct registry_context *ctx2,
256 const struct reg_diff_callbacks *callbacks,
257 void *callback_data)
258 {
259 int i;
260 WERROR error;
261
262 for (i = 0; reg_predefined_keys[i].name; i++) {
263 struct registry_key *r1 = NULL, *r2 = NULL;
264
265 error = reg_get_predefined_key(ctx1,
266 reg_predefined_keys[i].handle, &r1);
267 if (!W_ERROR_IS_OK(error) &&
268 !W_ERROR_EQUAL(error, WERR_BADFILE)) {
269 DEBUG(0, ("Unable to open hive %s for backend 1\n",
270 reg_predefined_keys[i].name));
271 continue;
272 }
273
274 error = reg_get_predefined_key(ctx2,
275 reg_predefined_keys[i].handle, &r2);
276 if (!W_ERROR_IS_OK(error) &&
277 !W_ERROR_EQUAL(error, WERR_BADFILE)) {
278 DEBUG(0, ("Unable to open hive %s for backend 2\n",
279 reg_predefined_keys[i].name));
280 continue;
281 }
282
283 error = reg_generate_diff_key(r1, r2,
284 reg_predefined_keys[i].name, callbacks,
285 callback_data);
286 if (!W_ERROR_IS_OK(error)) {
287 DEBUG(0, ("Unable to determine diff: %s\n",
288 win_errstr(error)));
289 return error;
290 }
291 }
292 if (callbacks->done != NULL) {
293 callbacks->done(callback_data);
294 }
295 return WERR_OK;
296 }
297
298 /**
299 * Load diff file
300 */
301 _PUBLIC_ WERROR reg_diff_load(const char *filename,
/* [<][>][^][v][top][bottom][index][help] */
302 struct smb_iconv_convenience *iconv_convenience,
303 const struct reg_diff_callbacks *callbacks,
304 void *callback_data)
305 {
306 int fd;
307 char hdr[4];
308
309 fd = open(filename, O_RDONLY, 0);
310 if (fd == -1) {
311 DEBUG(0, ("Error opening registry patch file `%s'\n",
312 filename));
313 return WERR_GENERAL_FAILURE;
314 }
315
316 if (read(fd, &hdr, 4) != 4) {
317 DEBUG(0, ("Error reading registry patch file `%s'\n",
318 filename));
319 return WERR_GENERAL_FAILURE;
320 }
321
322 /* Reset position in file */
323 lseek(fd, 0, SEEK_SET);
324 #if 0 /* These backends are not supported yet. */
325 if (strncmp(hdr, "CREG", 4) == 0) {
326 /* Must be a W9x CREG Config.pol file */
327 return reg_creg_diff_load(diff, fd);
328 } else if (strncmp(hdr, "regf", 4) == 0) {
329 /* Must be a REGF NTConfig.pol file */
330 return reg_regf_diff_load(diff, fd);
331 } else
332 #endif
333 if (strncmp(hdr, "PReg", 4) == 0) {
334 /* Must be a GPO Registry.pol file */
335 return reg_preg_diff_load(fd, iconv_convenience, callbacks, callback_data);
336 } else {
337 /* Must be a normal .REG file */
338 return reg_dotreg_diff_load(fd, iconv_convenience, callbacks, callback_data);
339 }
340 }
341
342 /**
343 * The reg_diff_apply functions
344 */
345 static WERROR reg_diff_apply_add_key(void *_ctx, const char *key_name)
/* [<][>][^][v][top][bottom][index][help] */
346 {
347 struct registry_context *ctx = (struct registry_context *)_ctx;
348 struct registry_key *tmp;
349 char *buf, *buf_ptr;
350 WERROR error;
351
352 /* Recursively create the path */
353 buf = talloc_strdup(ctx, key_name);
354 buf_ptr = buf;
355
356 while (*buf_ptr++ != '\0' ) {
357 if (*buf_ptr == '\\') {
358 *buf_ptr = '\0';
359 error = reg_key_add_abs(ctx, ctx, buf, 0, NULL, &tmp);
360
361 if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
362 !W_ERROR_IS_OK(error)) {
363 DEBUG(0, ("Error adding new key '%s': %s\n",
364 key_name, win_errstr(error)));
365 return error;
366 }
367 *buf_ptr++ = '\\';
368 }
369 }
370
371 /* Add the key */
372 error = reg_key_add_abs(ctx, ctx, key_name, 0, NULL, &tmp);
373
374 if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
375 !W_ERROR_IS_OK(error)) {
376 DEBUG(0, ("Error adding new key '%s': %s\n",
377 key_name, win_errstr(error)));
378 return error;
379 }
380 return WERR_OK;
381 }
382
383 static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name)
/* [<][>][^][v][top][bottom][index][help] */
384 {
385 struct registry_context *ctx = (struct registry_context *)_ctx;
386
387 /* We can't proof here for success, because a common superkey could */
388 /* have been deleted before the subkey's (diff order). This removed */
389 /* therefore all childs recursively and the "WERR_BADFILE" result is */
390 /* expected. */
391
392 reg_key_del_abs(ctx, key_name);
393
394 return WERR_OK;
395 }
396
397 static WERROR reg_diff_apply_set_value(void *_ctx, const char *path,
/* [<][>][^][v][top][bottom][index][help] */
398 const char *value_name,
399 uint32_t value_type, DATA_BLOB value)
400 {
401 struct registry_context *ctx = (struct registry_context *)_ctx;
402 struct registry_key *tmp;
403 WERROR error;
404
405 /* Open key */
406 error = reg_open_key_abs(ctx, ctx, path, &tmp);
407
408 if (W_ERROR_EQUAL(error, WERR_BADFILE)) {
409 DEBUG(0, ("Error opening key '%s'\n", path));
410 return error;
411 }
412
413 /* Set value */
414 error = reg_val_set(tmp, value_name,
415 value_type, value);
416 if (!W_ERROR_IS_OK(error)) {
417 DEBUG(0, ("Error setting value '%s'\n", value_name));
418 return error;
419 }
420
421 return WERR_OK;
422 }
423
424 static WERROR reg_diff_apply_del_value(void *_ctx, const char *key_name,
/* [<][>][^][v][top][bottom][index][help] */
425 const char *value_name)
426 {
427 struct registry_context *ctx = (struct registry_context *)_ctx;
428 struct registry_key *tmp;
429 WERROR error;
430
431 /* Open key */
432 error = reg_open_key_abs(ctx, ctx, key_name, &tmp);
433
434 if (!W_ERROR_IS_OK(error)) {
435 DEBUG(0, ("Error opening key '%s'\n", key_name));
436 return error;
437 }
438
439 error = reg_del_value(tmp, value_name);
440 if (!W_ERROR_IS_OK(error)) {
441 DEBUG(0, ("Error deleting value '%s'\n", value_name));
442 return error;
443 }
444
445
446 return WERR_OK;
447 }
448
449 static WERROR reg_diff_apply_del_all_values(void *_ctx, const char *key_name)
/* [<][>][^][v][top][bottom][index][help] */
450 {
451 struct registry_context *ctx = (struct registry_context *)_ctx;
452 struct registry_key *key;
453 WERROR error;
454 const char* value_name;
455
456 error = reg_open_key_abs(ctx, ctx, key_name, &key);
457
458 if (!W_ERROR_IS_OK(error)) {
459 DEBUG(0, ("Error opening key '%s'\n", key_name));
460 return error;
461 }
462
463 W_ERROR_NOT_OK_RETURN(reg_key_get_info(ctx, key, NULL,
464 NULL, NULL, NULL, NULL, NULL, NULL));
465
466 while (W_ERROR_IS_OK(reg_key_get_value_by_index(
467 ctx, key, 0, &value_name, NULL, NULL))) {
468 error = reg_del_value(key, value_name);
469 if (!W_ERROR_IS_OK(error)) {
470 DEBUG(0, ("Error deleting value '%s'\n", value_name));
471 return error;
472 }
473 }
474
475 return WERR_OK;
476 }
477
478 /**
479 * Apply diff to a registry context
480 */
481 _PUBLIC_ WERROR reg_diff_apply(struct registry_context *ctx,
/* [<][>][^][v][top][bottom][index][help] */
482 struct smb_iconv_convenience *iconv_convenience,
483 const char *filename)
484 {
485 struct reg_diff_callbacks callbacks;
486
487 callbacks.add_key = reg_diff_apply_add_key;
488 callbacks.del_key = reg_diff_apply_del_key;
489 callbacks.set_value = reg_diff_apply_set_value;
490 callbacks.del_value = reg_diff_apply_del_value;
491 callbacks.del_all_values = reg_diff_apply_del_all_values;
492 callbacks.done = NULL;
493
494 return reg_diff_load(filename, iconv_convenience,
495 &callbacks, ctx);
496 }