dns: Use new DNS debugclass in DNS server
[kai/samba.git] / source4 / lib / registry / patchfile.c
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 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         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  */
382 static 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
426 static 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
440 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         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
469 static 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
495 static 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 }