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