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