source: vendor/current/source4/lib/registry/patchfile.c

Last change on this file was 988, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.3

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