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