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 36 : 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 36 : unsigned int i;
46 36 : struct registry_key *t1 = NULL, *t2 = NULL;
47 36 : char *tmppath;
48 36 : const char *keyname1;
49 36 : WERROR error, error1, error2;
50 36 : TALLOC_CTX *mem_ctx = talloc_init("writediff");
51 36 : uint32_t old_num_subkeys, old_num_values,
52 : new_num_subkeys, new_num_values;
53 :
54 36 : if (oldkey != NULL) {
55 8 : error = reg_key_get_info(mem_ctx, oldkey, NULL,
56 : &old_num_subkeys, &old_num_values,
57 : NULL, NULL, NULL, NULL);
58 8 : 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 28 : old_num_subkeys = 0;
66 28 : old_num_values = 0;
67 : }
68 :
69 : /* Subkeys that were changed or deleted */
70 40 : for (i = 0; i < old_num_subkeys; i++) {
71 4 : error1 = reg_key_get_subkey_by_index(mem_ctx, oldkey, i,
72 : &keyname1, NULL, NULL);
73 4 : 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 4 : if (newkey != NULL) {
80 2 : error2 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
81 : } else {
82 2 : error2 = WERR_FILE_NOT_FOUND;
83 2 : t2 = NULL;
84 : }
85 :
86 4 : 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 4 : tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
96 4 : 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 4 : if (!W_ERROR_IS_OK(error2))
102 4 : callbacks->del_key(callback_data, tmppath);
103 :
104 : /* perform here also the recursive invocation */
105 4 : error1 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
106 4 : 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 4 : reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
113 :
114 4 : talloc_free(tmppath);
115 : }
116 :
117 36 : if (newkey != NULL) {
118 16 : error = reg_key_get_info(mem_ctx, newkey, NULL,
119 : &new_num_subkeys, &new_num_values,
120 : NULL, NULL, NULL, NULL);
121 16 : 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 20 : new_num_subkeys = 0;
129 20 : new_num_values = 0;
130 : }
131 :
132 : /* Subkeys that were added */
133 48 : for(i = 0; i < new_num_subkeys; i++) {
134 12 : error1 = reg_key_get_subkey_by_index(mem_ctx, newkey, i,
135 : &keyname1, NULL, NULL);
136 12 : 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 12 : if (oldkey != NULL) {
144 2 : error2 = reg_open_key(mem_ctx, oldkey, keyname1, &t1);
145 :
146 2 : if (W_ERROR_IS_OK(error2))
147 0 : continue;
148 : } else {
149 10 : error2 = WERR_FILE_NOT_FOUND;
150 10 : t1 = NULL;
151 : }
152 :
153 12 : 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 12 : tmppath = talloc_asprintf(mem_ctx, "%s\\%s", path, keyname1);
162 12 : 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 12 : callbacks->add_key(callback_data, tmppath);
168 :
169 : /* perform here also the recursive invocation */
170 12 : error1 = reg_open_key(mem_ctx, newkey, keyname1, &t2);
171 12 : 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 12 : reg_generate_diff_key(t1, t2, tmppath, callbacks, callback_data);
178 :
179 12 : talloc_free(tmppath);
180 : }
181 :
182 : /* Values that were added or changed */
183 38 : for(i = 0; i < new_num_values; i++) {
184 2 : const char *name;
185 2 : uint32_t type1, type2;
186 2 : DATA_BLOB contents1 = { NULL, 0 }, contents2 = { NULL, 0 };
187 :
188 2 : error1 = reg_key_get_value_by_index(mem_ctx, newkey, i,
189 : &name, &type1, &contents1);
190 2 : 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 2 : 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 2 : if (!W_ERROR_IS_OK(error2)
205 2 : && !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 2 : 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 2 : callbacks->set_value(callback_data, path, name,
222 : type1, contents1);
223 :
224 2 : talloc_free(discard_const_p(char, name));
225 2 : talloc_free(contents1.data);
226 2 : talloc_free(contents2.data);
227 : }
228 :
229 : /* Values that were deleted */
230 36 : for (i = 0; i < old_num_values; i++) {
231 0 : const char *name;
232 0 : 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 36 : talloc_free(mem_ctx);
270 36 : return WERR_OK;
271 : }
272 :
273 : /**
274 : * Generate diff between two registry contexts
275 : */
276 2 : _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 2 : unsigned int i;
282 2 : WERROR error;
283 :
284 20 : for (i = 0; reg_predefined_keys[i].name; i++) {
285 18 : struct registry_key *r1 = NULL, *r2 = NULL;
286 :
287 36 : error = reg_get_predefined_key(ctx1,
288 18 : reg_predefined_keys[i].handle, &r1);
289 18 : 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 18 : error = reg_get_predefined_key(ctx2,
297 0 : reg_predefined_keys[i].handle, &r2);
298 18 : 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 18 : 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 18 : if ((r1 != NULL) && (r2 == NULL)) {
316 0 : callbacks->del_key(callback_data,
317 0 : reg_predefined_keys[i].name);
318 : }
319 :
320 18 : error = reg_generate_diff_key(r1, r2,
321 0 : reg_predefined_keys[i].name, callbacks,
322 : callback_data);
323 18 : 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 2 : if (callbacks->done != NULL) {
330 2 : callbacks->done(callback_data);
331 : }
332 2 : return WERR_OK;
333 : }
334 :
335 : /**
336 : * Load diff file
337 : */
338 207 : _PUBLIC_ WERROR reg_diff_load(const char *filename,
339 : const struct reg_diff_callbacks *callbacks,
340 : void *callback_data)
341 : {
342 25 : int fd;
343 25 : char hdr[4];
344 :
345 207 : fd = open(filename, O_RDONLY, 0);
346 207 : 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 207 : 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 207 : 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 207 : if (strncmp(hdr, "PReg", 4) == 0) {
371 : /* Must be a GPO Registry.pol file */
372 1 : return reg_preg_diff_load(fd, callbacks, callback_data);
373 : } else {
374 : /* Must be a normal .REG file */
375 206 : return reg_dotreg_diff_load(fd, callbacks, callback_data);
376 : }
377 : }
378 :
379 : /**
380 : * The reg_diff_apply functions
381 : */
382 4107 : static WERROR reg_diff_apply_add_key(void *_ctx, const char *key_name)
383 : {
384 4107 : struct registry_context *ctx = (struct registry_context *)_ctx;
385 467 : struct registry_key *tmp;
386 467 : char *buf, *buf_ptr;
387 467 : WERROR error;
388 :
389 : /* Recursively create the path */
390 4107 : buf = talloc_strdup(ctx, key_name);
391 4107 : W_ERROR_HAVE_NO_MEMORY(buf);
392 3640 : buf_ptr = buf;
393 :
394 181619 : while (*buf_ptr++ != '\0' ) {
395 176872 : if (*buf_ptr == '\\') {
396 10687 : *buf_ptr = '\0';
397 10687 : error = reg_key_add_abs(ctx, ctx, buf, 0, NULL, &tmp);
398 :
399 10687 : if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
400 6370 : !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 10687 : *buf_ptr++ = '\\';
406 10687 : talloc_free(tmp);
407 : }
408 : }
409 :
410 4107 : talloc_free(buf);
411 :
412 : /* Add the key */
413 4107 : error = reg_key_add_abs(ctx, ctx, key_name, 0, NULL, &tmp);
414 :
415 4107 : if (!W_ERROR_EQUAL(error, WERR_ALREADY_EXISTS) &&
416 3094 : !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 4107 : talloc_free(tmp);
422 :
423 4107 : return WERR_OK;
424 : }
425 :
426 4 : static WERROR reg_diff_apply_del_key(void *_ctx, const char *key_name)
427 : {
428 4 : 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 4 : reg_key_del_abs(ctx, key_name);
436 :
437 4 : return WERR_OK;
438 : }
439 :
440 617 : 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 617 : struct registry_context *ctx = (struct registry_context *)_ctx;
445 71 : struct registry_key *tmp;
446 71 : WERROR error;
447 :
448 : /* Open key */
449 617 : error = reg_open_key_abs(ctx, ctx, path, &tmp);
450 :
451 617 : 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 617 : error = reg_val_set(tmp, value_name,
458 : value_type, value);
459 617 : if (!W_ERROR_IS_OK(error)) {
460 0 : DEBUG(0, ("Error setting value '%s'\n", value_name));
461 0 : return error;
462 : }
463 :
464 617 : talloc_free(tmp);
465 :
466 617 : 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 0 : struct registry_key *tmp;
474 0 : 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 0 : struct registry_key *key;
499 0 : WERROR error;
500 0 : 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 207 : _PUBLIC_ WERROR reg_diff_apply(struct registry_context *ctx,
531 : const char *filename)
532 : {
533 25 : struct reg_diff_callbacks callbacks;
534 :
535 207 : callbacks.add_key = reg_diff_apply_add_key;
536 207 : callbacks.del_key = reg_diff_apply_del_key;
537 207 : callbacks.set_value = reg_diff_apply_set_value;
538 207 : callbacks.del_value = reg_diff_apply_del_value;
539 207 : callbacks.del_all_values = reg_diff_apply_del_all_values;
540 207 : callbacks.done = NULL;
541 :
542 207 : return reg_diff_load(filename, &callbacks, ctx);
543 : }
|