9bb7296db541acb0952641ca0dd73dfae132af29
[obnox/samba/samba-obnox.git] / source3 / utils / net_registry_check.c
1 /*
2  * Samba Unix/Linux SMB client library
3  *
4  * Copyright (C) Gregor Beck 2011
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /**
21  * @brief  Check the registry database.
22  * @author Gregor Beck <gb@sernet.de>
23  * @date   Mar 2011
24  */
25
26 #include "net_registry_check.h"
27
28 #include "includes.h"
29 #include "system/filesys.h"
30 #include "lib/dbwrap/dbwrap.h"
31 #include "lib/dbwrap/dbwrap_open.h"
32 #include "lib/dbwrap/dbwrap_rbt.h"
33 #include "net.h"
34 #include "libcli/security/dom_sid.h"
35 #include "libcli/security/secdesc.h"
36 #include "cbuf.h"
37 #include "srprs.h"
38 #include <termios.h>
39 #include "util_tdb.h"
40 #include "registry/reg_db.h"
41 #include "libcli/registry/util_reg.h"
42 #include "registry/reg_parse_internal.h"
43 #include "interact.h"
44
45 /*
46   check tree:
47   + every key has a subkeylist
48   + every key is referenced by the subkeylist of its parent
49   check path:
50   + starts with valid hive
51   + UTF-8 (option to convert ???)
52   + only uppercase
53   + separator ???
54   check value:
55   + REG_DWORD has size 4
56   + REG_QWORD has size 8
57   + STRINGS are zero terminated UTF-16
58 */
59
60 struct regval {
61         char *name;
62         uint32_t type;
63         DATA_BLOB data;
64 };
65
66 struct regkey {
67         char *name;
68         char *path;
69         bool has_subkeylist;
70         bool needs_update;
71         struct regkey *parent;
72         size_t nsubkeys;
73         struct regkey **subkeys;
74         size_t nvalues;
75         struct regval **values;
76         struct security_descriptor *sd;
77 };
78
79 struct check_ctx {
80         char *fname;
81         struct check_options opt;
82
83         uint32_t version;
84         char sep;
85         struct db_context *idb;
86         struct db_context *odb;
87
88         struct regkey *root; /*dummy key to hold all basekeys*/
89         struct db_context *reg;
90         struct db_context *del;
91
92         bool transaction;
93         char auto_action;
94         char default_action;
95 };
96
97 static void* talloc_array_append(void *mem_ctx, void* array[], void *ptr)
98 {
99         size_t size = array ? talloc_array_length(array) : 1;
100         void **tmp = talloc_realloc(mem_ctx, array, void*, size + 1);
101         if (tmp == NULL) {
102                 talloc_free(array);
103                 return NULL;
104         }
105         tmp[size-1] = ptr;
106         tmp[size] = NULL;
107         return tmp;
108 }
109
110 static void regkey_add_subkey(struct regkey *key, struct regkey *subkey)
111 {
112         key->subkeys = (struct regkey**)
113                 talloc_array_append(key, (void**)key->subkeys, subkey);
114         if (key->subkeys != NULL) {
115                 key->nsubkeys++;
116         }
117 }
118
119 static struct regval* regval_copy(TALLOC_CTX *mem_ctx, const struct regval *val)
120 {
121         struct regval *ret = talloc_zero(mem_ctx, struct regval);
122         if (ret == NULL) {
123                 goto fail;
124         }
125
126         ret->name = talloc_strdup(ret, val->name);
127         if (ret->name == NULL) {
128                 goto fail;
129         }
130
131         ret->data = data_blob_dup_talloc(ret, val->data);
132         if (ret->data.data == NULL) {
133                 goto fail;
134         }
135
136         ret->type = val->type;
137
138         return ret;
139 fail:
140         talloc_free(ret);
141         return NULL;
142 }
143
144 static void regkey_add_regval(struct regkey *key, struct regval *val)
145 {
146         key->values = (struct regval**)
147                 talloc_array_append(key, (void**)key->values, val);
148         if (key->values != NULL) {
149                 key->nvalues++;
150         }
151 }
152
153 static bool tdb_data_read_uint32(TDB_DATA *buf, uint32_t *result)
154 {
155         const size_t len = sizeof(uint32_t);
156         if (buf->dsize >= len) {
157                 *result = IVAL(buf->dptr, 0);
158                 buf->dptr += len;
159                 buf->dsize -= len;
160                 return true;
161         }
162         return false;
163 }
164
165 static bool tdb_data_read_cstr(TDB_DATA *buf, char **result)
166 {
167         const size_t len = strnlen((char*)buf->dptr, buf->dsize) + 1;
168         if (buf->dsize >= len) {
169                 *result = (char*)buf->dptr;
170                 buf->dptr += len;
171                 buf->dsize -= len;
172                 return true;
173         }
174         return false;
175 }
176
177 static bool tdb_data_read_blob(TDB_DATA *buf, DATA_BLOB *result)
178 {
179         TDB_DATA tmp = *buf;
180         uint32_t len;
181         if (!tdb_data_read_uint32(&tmp, &len)) {
182                 return false;
183         }
184         if (tmp.dsize >= len) {
185                 *buf = tmp;
186                 result->data   = tmp.dptr;
187                 result->length = len;
188                 buf->dptr += len;
189                 buf->dsize -= len;
190                 return true;
191         }
192         return false;
193 }
194
195 static bool tdb_data_read_regval(TDB_DATA *buf, struct regval *result)
196 {
197         TDB_DATA tmp = *buf;
198         struct regval value;
199         if (!tdb_data_read_cstr(&tmp, &value.name)
200             || !tdb_data_read_uint32(&tmp, &value.type)
201             || !tdb_data_read_blob(&tmp, &value.data))
202         {
203                 return false;
204         }
205         *buf = tmp;
206         *result = value;
207         return true;
208 }
209
210 static bool tdb_data_is_empty(TDB_DATA d) {
211         return (d.dptr == NULL) || (d.dsize == 0);
212 }
213
214 static bool tdb_data_is_cstr(TDB_DATA d) {
215         if (tdb_data_is_empty(d) || (d.dptr[d.dsize-1] != '\0')) {
216                 return false;
217         }
218         return rawmemchr(d.dptr, '\0') == &d.dptr[d.dsize-1];
219 }
220
221 static char* tdb_data_print(TALLOC_CTX *mem_ctx, TDB_DATA d)
222 {
223         if (!tdb_data_is_empty(d)) {
224                 char *ret = NULL;
225                 cbuf *ost = cbuf_new(mem_ctx);
226                 int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
227                 if (len != -1) {
228                         cbuf_swapptr(ost, &ret, 0);
229                         talloc_steal(mem_ctx, ret);
230                 }
231                 talloc_free(ost);
232                 return ret;
233         }
234         return talloc_strdup(mem_ctx, "<NULL>");
235 }
236
237
238 static TDB_DATA cbuf_make_tdb_data(cbuf *b)
239 {
240         return make_tdb_data((void*)cbuf_gets(b, 0), cbuf_getpos(b));
241 }
242
243 static void remove_all(char *str, char c)
244 {
245         char *out=str;
246         while (*str) {
247                 if (*str != c) {
248                         *out = *str;
249                         out++;
250                 }
251                 str++;
252         }
253         *out = '\0';
254 }
255
256 static void remove_runs(char *str, char c)
257 {
258         char *out=str;
259         while (*str) {
260                 *out = *str;
261                 if (*str == c) {
262                         while (*str == c) {
263                                 str++;
264                         }
265                 } else {
266                         str++;
267                 }
268                 out++;
269         }
270         *out = '\0';
271 }
272
273
274 static char* parent_path(const char *path, char sep)
275 {
276         const char *p = strrchr(path, sep);
277         return p ? talloc_strndup(talloc_tos(), path, p-path) : NULL;
278 }
279
280 /* return the regkey corresponding to path, create if not yet existing */
281 static struct regkey*
282 check_ctx_lookup_key(struct check_ctx *ctx, const char *path) {
283         struct regkey *ret = NULL;
284         NTSTATUS status;
285         TDB_DATA val;
286
287         if ( path == NULL) {
288                 return ctx->root;
289         }
290
291         status = dbwrap_fetch(ctx->reg, ctx, string_term_tdb_data(path), &val);
292         if (!NT_STATUS_IS_OK(status)) {
293                 return NULL;
294         }
295         if (val.dptr != NULL) {
296                 if (ctx->opt.verbose) {
297                         printf("Open: %s\n", path);
298                 }
299                 ret = *(struct regkey**)val.dptr;
300         } else {
301                 /* not yet existing, create */
302                 char *pp;
303                 if (ctx->opt.verbose) {
304                         printf("New: %s\n", path);
305                 }
306                 ret = talloc_zero(ctx, struct regkey);
307                 if (ret == NULL) {
308                         DEBUG(0, ("Out of memory!\n"));
309                         goto done;
310                 }
311                 ret->path = talloc_strdup(ret, path);
312
313                 pp = parent_path(path, ctx->sep);
314                 ret->parent = check_ctx_lookup_key(ctx, pp);
315                 regkey_add_subkey(ret->parent, ret);
316                 TALLOC_FREE(pp);
317
318                 /* the dummy root key has no subkeylist so set the name */
319                 if (ret->parent == ctx->root) {
320                         ret->name = talloc_strdup(ret, path);
321                 }
322
323                 dbwrap_store(ctx->reg, string_term_tdb_data(path),
324                              make_tdb_data((void*)&ret, sizeof(ret)), 0);
325         }
326 done:
327         talloc_free(val.dptr);
328         return ret;
329 }
330
331 static struct check_ctx* check_ctx_create(TALLOC_CTX *mem_ctx, const char *db,
332                                           const struct check_options *opt)
333 {
334         struct check_ctx *ctx = talloc_zero(mem_ctx, struct check_ctx);
335
336         ctx->opt = *opt;
337         ctx->reg = db_open_rbt(ctx);
338         ctx->del = db_open_rbt(ctx);
339         ctx->root = talloc_zero(ctx, struct regkey);
340         ctx->fname = talloc_strdup(ctx, db);
341
342         if (opt->automatic && (opt->output == NULL)) {
343                 ctx->opt.repair = true;
344                 ctx->opt.output = ctx->fname;
345         }
346
347         if (opt->repair) {
348                 if (opt->output) {
349                         d_fprintf(stderr, "You can not specify --output "
350                                   "with --repair\n");
351                         goto fail;
352                 } else {
353                         ctx->opt.output = ctx->fname;
354                 }
355         }
356
357         ctx->default_action = opt->automatic ? 'd' : 'r';
358         return ctx;
359 fail:
360         talloc_free(ctx);
361         return NULL;
362 }
363
364 static bool check_ctx_open_output(struct check_ctx *ctx)
365 {
366         int oflags = O_RDWR | O_CREAT ;
367
368         if (ctx->opt.output == NULL) {
369                 return true;
370         }
371
372         if (!ctx->opt.repair) {
373                 if (!ctx->opt.wipe) {
374                         oflags |= O_EXCL;
375                 }
376                 ctx->opt.wipe = true;
377         }
378
379         ctx->odb = db_open(ctx, ctx->opt.output, 0, TDB_DEFAULT, oflags, 0644);
380         if (ctx->odb == NULL) {
381                 d_fprintf(stderr,
382                           _("Could not open db (%s) for writing: %s\n"),
383                           ctx->opt.output, strerror(errno));
384                 return false;
385         }
386         return true;
387 }
388
389
390 static bool check_ctx_open_input(struct check_ctx *ctx) {
391         ctx->idb = db_open(ctx, ctx->fname, 0, TDB_DEFAULT, O_RDONLY, 0);
392         if (ctx->idb == NULL) {
393                 d_fprintf(stderr,
394                           _("Could not open db (%s) for reading: %s\n"),
395                           ctx->fname, strerror(errno));
396                 return false;
397         }
398         return true;
399 }
400
401 static bool check_ctx_transaction_start(struct check_ctx *ctx) {
402         if (ctx->odb == NULL) {
403                 return true;
404         }
405         if (dbwrap_transaction_start(ctx->odb) != 0) {
406                 DEBUG(0, ("transaction_start failed\n"));
407                 return false;
408         }
409         ctx->transaction = true;
410         return true;
411 }
412
413 static void check_ctx_transaction_stop(struct check_ctx *ctx, bool ok) {
414         if (!ctx->transaction) {
415                 return;
416         }
417         if (!ctx->opt.test && ok) {
418                 d_printf("Commiting changes\n");
419                 if (dbwrap_transaction_commit(ctx->odb) != 0) {
420                         DEBUG(0, ("transaction_commit failed\n"));
421                 }
422         } else {
423                 d_printf("Discarding changes\n");
424                 dbwrap_transaction_cancel(ctx->odb);
425         }
426 }
427
428 static bool read_info(struct check_ctx *ctx, const char *key, TDB_DATA val)
429 {
430         if (val.dsize==sizeof(uint32_t) && strcmp(key, "version")==0) {
431                 uint32_t v = IVAL(val.dptr, 0);
432                 printf("INFO: %s = %d\n", key, v);
433                 return true;
434         }
435         printf("INFO: %s = <invalid>\n", key);
436         return false;
437 }
438
439 static bool is_all_upper(const char *str) {
440         bool ret;
441         char *tmp = talloc_strdup(talloc_tos(), str);
442         strupper_m(tmp);
443         ret = (strcmp(tmp, str) == 0);
444         talloc_free(tmp);
445         return ret;
446 }
447
448 static void move_to_back(struct regkey *key, struct regkey *subkey)
449 {
450         struct regkey **ptr;
451         size_t nidx;
452
453         DEBUG(5, ("Move to back subkey \"%s\" of \"%s\"\n",
454                   subkey->path, key->path));
455
456         for (ptr=key->subkeys; *ptr != subkey; ptr++)
457                 ;
458
459         nidx = ptr + 1 - key->subkeys;
460         memmove(ptr, ptr+1, (key->nsubkeys - nidx) * sizeof(*ptr));
461
462         key->subkeys[key->nsubkeys-1] = subkey;
463 }
464
465 static void set_subkey_name(struct check_ctx *ctx, struct regkey *key,
466                             const char *name, int nlen)
467 {
468         char *path = key->path;
469         TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
470         char *p;
471         struct regkey *subkey;
472         char *nname = talloc_strndup(mem_ctx, name, nlen);
473         remove_all(nname, ctx->sep);
474
475         if (strncmp(name, nname, nlen) != 0) {
476                 /* XXX interaction: delete/edit */
477                 printf("Warning: invalid name: \"%s\" replace with \"%s\"\n",
478                        name, nname);
479                 key->needs_update = true;
480         }
481         p = talloc_asprintf_strupper_m(mem_ctx, "%s%c%s",
482                                        path, ctx->sep, nname);
483         subkey = check_ctx_lookup_key(ctx, p);
484         if (subkey->name) {
485                 bool do_replace = false;
486
487                 if (strcmp(subkey->name, nname) != 0) {
488                         int action;
489                         char default_action;
490
491                         if (is_all_upper(nname)) {
492                                 default_action = 'o';
493                         } else {
494                                 default_action = 'n';
495                         }
496
497                         printf("Conflicting subkey names of [%s]: "
498                                "old: \"%s\", new: \"%s\"\n",
499                                key->path, subkey->name, nname);
500
501                         if (ctx->opt.output == NULL || ctx->opt.automatic) {
502                                 action = default_action;
503                         } else {
504                                 do {
505                                         action = interact_prompt(
506                                                 "choose spelling [o]ld, [n]ew,"
507                                                 "or [e]dit", "one",
508                                                 default_action);
509                                         if (action == 'e') {
510                                                 printf("Sorry, edit is not yet "
511                                                        "implemented here...\n");
512                                         }
513                                 } while (action == 'e');
514                         }
515
516                         if (action == 'n') {
517                                 do_replace = true;
518                         }
519                 }
520
521                 if (do_replace) {
522                         if (ctx->opt.verbose) {
523                                 printf("Replacing name: %s: \"%s\""
524                                        " -> \"%s\"\n", path,
525                                        subkey->name, nname);
526                         }
527                         TALLOC_FREE(subkey->name);
528                         subkey->name = talloc_steal(subkey, nname);
529                         key->needs_update = true;
530                 }
531         } else {
532                 if (ctx->opt.verbose) {
533                         printf("Set name: %s: \"%s\"\n", path, nname);
534                 }
535                 subkey->name = talloc_steal(subkey, nname);
536         }
537
538         move_to_back(key, subkey);
539         TALLOC_FREE(mem_ctx);
540 }
541
542 static void
543 read_subkeys(struct check_ctx *ctx, const char *path, TDB_DATA val, bool update)
544 {
545         uint32_t num_items, found_items = 0;
546         char *subkey;
547         struct regkey *key = check_ctx_lookup_key(ctx, path);
548
549         key->needs_update |= update;
550
551         /* printf("SUBKEYS: %s\n", path); */
552         if (key->has_subkeylist) {
553                 printf("Duplicate subkeylist \"%s\"\n",
554                        path);
555                 found_items = key->nsubkeys;
556         }
557
558         /* exists as defined by regdb_key_exists() */
559         key->has_subkeylist = true;
560
561         /* name is set if a key is referenced by the */
562         /* subkeylist of its parent. */
563
564         if (!tdb_data_read_uint32(&val, &num_items) ) {
565                 printf("Invalid subkeylist: \"%s\"\n", path);
566                 return;
567         }
568
569         while (tdb_data_read_cstr(&val, &subkey)) {
570                 /* printf(" SUBKEY: %s\n", subkey); */
571                 set_subkey_name(ctx, key, subkey, strlen(subkey));
572                 found_items++;
573         }
574
575         if (val.dsize != 0) {
576                 printf("Subkeylist of \"%s\": trailing: \"%.*s\"\n",
577                        path, (int)val.dsize, val.dptr);
578                 /* ask: best effort, delete or edit?*/
579                 set_subkey_name(ctx, key, (char*)val.dptr, val.dsize);
580                 found_items++;
581                 key->needs_update = true;
582         }
583
584         if (num_items != found_items) {
585                 printf("Subkeylist of \"%s\": invalid number of subkeys, "
586                        "expected: %d got: %d\n", path, num_items, found_items);
587                 key->needs_update = true;
588         }
589
590 }
591
592 static void read_values(struct check_ctx *ctx, const char *path, TDB_DATA val)
593 {
594         struct regkey *key = check_ctx_lookup_key(ctx, path);
595         uint32_t num_items, found_items;
596         struct regval value;
597
598         /* printf("VALUES: %s\n", path); */
599
600         if (!tdb_data_read_uint32(&val, &num_items) ) {
601                 printf("Invalid valuelist: \"%s\"\n", path);
602                 return;
603         }
604
605         found_items=0;
606         while (tdb_data_read_regval(&val, &value)) {
607                 /* printf(" VAL: %s type: %s(%d) length: %d\n", value.name, */
608                 /*        str_regtype(value.type), value.type, */
609                 /*        (int)value.data.length); */
610                 regkey_add_regval(key, regval_copy(key, &value));
611                 found_items++;
612         }
613
614         if (num_items != found_items) {
615                 printf("Valuelist of \"%s\": invalid number of values, "
616                        "expected: %d got: %d\n", path, num_items, found_items);
617                 key->needs_update = true;
618         }
619
620         if (val.dsize != 0) {
621                 printf("Valuelist of \"%s\": trailing: \"%*s\"\n", path,
622                        (int)val.dsize, val.dptr);
623                 key->needs_update = true;
624                 /* XXX best effort ??? */
625                 /* ZERO_STRUCT(value); */
626                 /* if (tdb_data_read_cstr(&val, &value.name) */
627                 /*     && tdb_data_read_uint32(&val, &value.type)) */
628                 /* { */
629                 /*      uint32_t len = -1; */
630                 /*      tdb_data_read_uint32(&val, &len); */
631                 /*      ... */
632                 /*      found_items ++; */
633                 /*      regkey_add_regval(key, regval_copy(key, value)); */
634                 /* } */
635         }
636         if (found_items == 0) {
637                 printf("Valuelist of \"%s\" empty\n", path);
638                 key->needs_update = true;
639         }
640 }
641
642 static bool read_sorted(struct check_ctx *ctx, const char *path, TDB_DATA val)
643 {
644         if (ctx->version >= 3) {
645                 return false;
646         }
647
648         if ((val.dptr == NULL) || (val.dsize<4)) {
649                 return false;
650         }
651
652         /* ToDo: check */
653         /* struct regkey *key = check_ctx_lookup_key(ctx, path); */
654         /* printf("SORTED: %s\n", path); */
655         return true;
656 }
657
658 static bool read_sd(struct check_ctx *ctx, const char *path, TDB_DATA val)
659 {
660         NTSTATUS status;
661         struct regkey *key = check_ctx_lookup_key(ctx, path);
662         /* printf("SD: %s\n", path); */
663
664         status = unmarshall_sec_desc(key, val.dptr, val.dsize, &key->sd);
665         if (!NT_STATUS_IS_OK(status)) {
666                 DEBUG(0, ("Failed to read SD of %s: %s\n",
667                           path, nt_errstr(status)));
668         }
669         return true;
670 }
671
672 static bool srprs_path(const char **ptr, const char* prefix, char sep,
673                        const char **ppath)
674 {
675         const char *path, *pos = *ptr;
676         if (prefix != NULL) {
677                 if (!srprs_str(&pos, prefix, -1) || !srprs_char(&pos, sep) ) {
678                         return false;
679                 }
680         }
681         path = pos;
682         if ( !srprs_hive(&pos, NULL) ) {
683                 return false;
684         }
685         if ( !srprs_eos(&pos) && !srprs_char(&pos, sep) ) {
686                 return false;
687         }
688         *ppath = path;
689         *ptr = rawmemchr(pos, '\0');
690         return true;
691 }
692
693 /* Fixme: this dosn't work in the general multibyte char case.
694    see string_replace()
695 */
696 static bool normalize_path_internal(char* path, char sep) {
697         size_t len = strlen(path);
698         const char *orig = talloc_strndup(talloc_tos(), path, len);
699         char *optr = path, *iptr = path;
700         bool changed;
701
702         while (*iptr == sep ) {
703                 iptr++;
704         }
705         while (*iptr) {
706                 *optr = *iptr;
707                 if (*iptr == sep) {
708                         while (*iptr == sep) {
709                                 iptr++;
710                         }
711                         if (*iptr) {
712                                 optr++;
713                         }
714                 } else {
715                         iptr++;
716                         optr++;
717                 }
718         }
719         *optr = '\0';
720
721         strupper_m(path);
722         changed = (strcmp(orig, path) != 0);
723         talloc_free(discard_const(orig));
724         return changed;
725 }
726
727 static bool normalize_path(char* path, char sep) {
728         static const char* SEPS = "\\/";
729         char* firstsep = strpbrk(path, SEPS);
730
731         assert (strchr(SEPS, sep));
732
733         if (firstsep && (*firstsep != sep)) {
734                 string_replace(path, *firstsep, sep);
735         }
736         return normalize_path_internal(path, sep);
737 }
738
739 static int check_tdb_action(struct db_record *rec, void *check_ctx)
740 {
741         struct check_ctx *ctx = (struct check_ctx*)check_ctx;
742         TALLOC_CTX *frame = talloc_stackframe();
743         TDB_DATA val = dbwrap_record_get_value(rec);
744         TDB_DATA rec_key = dbwrap_record_get_key(rec);
745         char *key;
746         bool invalid_path = false;
747         bool once_more;
748
749         if (!tdb_data_is_cstr(rec_key)) {
750                 printf("Key is not zero terminated: \"%.*s\"\ntry to go on.\n",
751                        (int)rec_key.dsize, rec_key.dptr);
752                 invalid_path = true;
753         }
754         key = talloc_strndup(frame, (char*)rec_key.dptr, rec_key.dsize);
755
756         do {
757                 const char *path, *pos = key;
758                 once_more = false;
759
760                 if (srprs_str(&pos, "INFO/", -1)) {
761                         if ( read_info(ctx, pos, val) ) {
762                                 break;
763                         }
764                         invalid_path = true;
765                         /* ask: mark invalid */
766                 } else if (srprs_str(&pos, "__db_sequence_number__", -1)) {
767                         printf("Skip key: \"%.*s\"\n",
768                                (int)rec_key.dsize, rec_key.dptr);
769                         /* skip: do nothing + break */
770                         break;
771
772                 } else if (normalize_path(key, ctx->sep)) {
773                         printf("Unnormal key: \"%.*s\"\n",
774                                (int)rec_key.dsize, rec_key.dptr);
775                         printf("Normalize to: \"%s\"\n", key);
776                         invalid_path = true;
777                 } else if (srprs_path(&pos, NULL,
778                                       ctx->sep, &path))
779                 {
780                         read_subkeys(ctx, path, val, invalid_path);
781                         break;
782                 } else if (srprs_path(&pos, REG_VALUE_PREFIX,
783                                       ctx->sep, &path))
784                 {
785                         read_values(ctx, path, val);
786                         break;
787                 } else if (srprs_path(&pos, REG_SECDESC_PREFIX,
788                                       ctx->sep, &path))
789                 {
790                         read_sd(ctx, path, val);
791                         break;
792                 } else if (srprs_path(&pos, REG_SORTED_SUBKEYS_PREFIX,
793                                       ctx->sep, &path))
794                 {
795                         if (!read_sorted(ctx, path, val)) {
796                                 /* delete: mark invalid + break */
797                                 printf("Invalid sorted subkeys for: \"%s\"\n", path);
798                                 invalid_path = true;
799                                 key = NULL;
800                         }
801                         break;
802                 } else {
803                         printf("Unrecognized key: \"%.*s\"\n",
804                                (int)rec_key.dsize, rec_key.dptr);
805                         invalid_path = true;
806                 }
807
808                 if (invalid_path) {
809                         int action;
810                         if (ctx->opt.output == NULL) {
811                                 action = 's';
812                         } else if (ctx->opt.automatic) {
813                                 action = (ctx->default_action == 'r') ? 'd' : 'r';
814                         } else if (ctx->auto_action != '\0') {
815                                 action = ctx->auto_action;
816                         } else {
817                                 action = interact_prompt("[s]kip,[S]kip all,"
818                                                          "[d]elete,[D]elete all"
819                                                          ",[e]dit,[r]etry"
820                                                          , "sder",
821                                                          ctx->default_action);
822                         }
823                         if (isupper(action)) {
824                                 action = tolower(action);
825                                 ctx->auto_action = action;
826                         }
827                         ctx->default_action = action;
828                         switch (action) {
829                         case 's': /* skip */
830                                 invalid_path = false;
831                                 break;
832                         case 'd': /* delete */
833                                 invalid_path = true;
834                                 key = NULL;
835                                 break;
836                         case 'e': /* edit */ {
837                                 char *p = interact_edit(frame, key);
838                                 if (p) {
839                                         talloc_free(key);
840                                         key = p;
841                                 }
842                         } /* fall through */
843                         case 'r': /* retry */
844                                 once_more = true;
845                                 break;
846                         }
847                 }
848         } while (once_more);
849
850         if (invalid_path) {
851                 dbwrap_store(ctx->del, rec_key, string_term_tdb_data(key), 0);
852         }
853
854         talloc_free(frame);
855         return 0;
856 }
857
858 static bool get_version(struct check_ctx *ctx) {
859         static const uint32_t curr_version = REGDB_CODE_VERSION;
860         uint32_t version = ctx->opt.version ? ctx->opt.version : curr_version;
861         uint32_t info_version = 0;
862         NTSTATUS status;
863
864         status = dbwrap_fetch_uint32(ctx->idb, "INFO/version", &info_version);
865         if (!NT_STATUS_IS_OK(status)) {
866                 printf("Warning: no INFO/version found!\n");
867                 /* info_version = guess_version(ctx); */
868         }
869
870         if (ctx->opt.version) {
871                 version = ctx->opt.version;
872         } else if (ctx->opt.implicit_db) {
873                 version = curr_version;
874         } else {
875                 version = info_version;
876         }
877
878         if (!version) {
879                 printf("Couldn't determine registry format version, "
880                        "specify with --version\n");
881                 return false;
882         }
883
884
885         if ( version != info_version ) {
886                 if (ctx->opt.force || !ctx->opt.repair) {
887                         printf("Warning: overwrite registry format "
888                                "version %d with %d\n", info_version, version);
889                 } else {
890                         printf("Warning: found registry format version %d but "
891                                "expected %d\n", info_version, version);
892                         return false;
893                 }
894         }
895
896         ctx->version = version;
897         ctx->sep = (version > 1) ? '\\' : '/';
898
899         return true;
900 }
901
902 static bool
903 dbwrap_store_verbose(struct db_context *db, const char *key, TDB_DATA nval)
904 {
905         TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
906         TDB_DATA oval;
907         NTSTATUS status;
908
909         status = dbwrap_fetch_bystring(db, mem_ctx, key, &oval);
910         if (!NT_STATUS_IS_OK(status)) {
911                 printf ("store %s failed to fetch old value: %s\n", key,
912                         nt_errstr(status));
913                 goto done;
914         }
915
916         if (!tdb_data_is_empty(oval) && !tdb_data_equal(nval, oval)) {
917                 printf("store %s:\n"
918                        "  overwrite: %s\n"
919                        "  with:      %s\n",
920                        key, tdb_data_print(mem_ctx, oval),
921                        tdb_data_print(mem_ctx, nval));
922         }
923
924         status = dbwrap_store_bystring(db, key, nval, 0);
925         if (!NT_STATUS_IS_OK(status)) {
926                 printf ("store %s failed: %s\n", key, nt_errstr(status));
927         }
928
929 done:
930         talloc_free(mem_ctx);
931         return NT_STATUS_IS_OK(status);
932 }
933
934
935 static int cmp_keynames(char **p1, char **p2)
936 {
937         return strcasecmp_m(*p1, *p2);
938 }
939
940 static bool
941 write_subkeylist(struct db_context *db, struct regkey *key, char sep)
942 {
943         cbuf *buf = cbuf_new(talloc_tos());
944         int i;
945         bool ret;
946
947         cbuf_putdw(buf, key->nsubkeys);
948
949         for (i=0; i < key->nsubkeys; i++) {
950                 struct regkey *subkey = key->subkeys[i];
951                 const char *name = subkey->name;
952                 if (name == NULL) {
953                         printf("Warning: no explicite name for key %s\n",
954                                subkey->path);
955                         name = strrchr_m(subkey->path, sep);
956                         assert(name);
957                         name ++;
958                 }
959                 cbuf_puts(buf, name, -1);
960                 cbuf_putc(buf, '\0');
961         }
962
963         ret = dbwrap_store_verbose(db, key->path, cbuf_make_tdb_data(buf));
964
965         talloc_free(buf);
966         return ret;
967 }
968
969 static bool write_sorted(struct db_context *db, struct regkey *key, char sep)
970 {
971         cbuf *buf = cbuf_new(talloc_tos());
972         char *path;
973         int i;
974         bool ret = false;
975         char **sorted = talloc_zero_array(buf, char*, key->nsubkeys);
976         int offset =  (1 + key->nsubkeys) * sizeof(uint32_t);
977
978         for (i=0; i < key->nsubkeys; i++) {
979                 sorted[i] = talloc_strdup_upper(sorted, key->subkeys[i]->name);
980         }
981         TYPESAFE_QSORT(sorted, key->nsubkeys, cmp_keynames);
982
983         cbuf_putdw(buf, key->nsubkeys);
984         for (i=0; i < key->nsubkeys; i++) {
985                 cbuf_putdw(buf, offset);
986                 offset += strlen(sorted[i]) + 1;
987         }
988         for (i=0; i < key->nsubkeys; i++) {
989                 cbuf_puts(buf, sorted[i], -1);
990                 cbuf_putc(buf, '\0');
991         }
992
993         path = talloc_asprintf(buf, "%s%c%s", REG_SORTED_SUBKEYS_PREFIX, sep,
994                                key->path);
995         if (path == NULL) {
996                 DEBUG(0, ("Out of memory!\n"));
997                 goto done;
998         }
999
1000         ret = dbwrap_store_verbose(db, path, cbuf_make_tdb_data(buf));
1001 done:
1002         talloc_free(buf);
1003         return ret;
1004 }
1005
1006 static bool write_values(struct db_context *db, struct regkey *key, char sep)
1007 {
1008         cbuf *buf = cbuf_new(talloc_tos());
1009         char *path;
1010         int i;
1011         bool ret = false;
1012
1013         cbuf_putdw(buf, key->nvalues);
1014         for (i=0; i < key->nvalues; i++) {
1015                 struct regval *val = key->values[i];
1016                 cbuf_puts(buf, val->name, -1);
1017                 cbuf_putc(buf, '\0');
1018                 cbuf_putdw(buf, val->type);
1019                 cbuf_putdw(buf, val->data.length);
1020                 cbuf_puts(buf, (void*)val->data.data, val->data.length);
1021         }
1022
1023         path = talloc_asprintf(buf, "%s%c%s", REG_VALUE_PREFIX, sep, key->path);
1024         if (path == NULL) {
1025                 DEBUG(0, ("Out of memory!\n"));
1026                 goto done;
1027         }
1028
1029         ret = dbwrap_store_verbose(db, path, cbuf_make_tdb_data(buf));
1030 done:
1031         talloc_free(buf);
1032         return ret;
1033 }
1034
1035 static bool write_sd(struct db_context *db, struct regkey *key, char sep)
1036 {
1037         TDB_DATA sd;
1038         NTSTATUS status;
1039         char *path;
1040         bool ret = false;
1041         TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
1042
1043         status = marshall_sec_desc(mem_ctx, key->sd, &sd.dptr, &sd.dsize);
1044         if (!NT_STATUS_IS_OK(status)) {
1045                 printf("marshall sec desc %s failed: %s\n",
1046                        key->path, nt_errstr(status));
1047                 goto done;
1048         }
1049         path = talloc_asprintf(mem_ctx, "%s%c%s", REG_SECDESC_PREFIX,
1050                                sep, key->path);
1051         if (path == NULL) {
1052                 DEBUG(0, ("Out of memory!\n"));
1053                 goto done;
1054         }
1055
1056         ret = dbwrap_store_verbose(db, path, sd);
1057 done:
1058         talloc_free(mem_ctx);
1059         return ret;
1060 }
1061
1062
1063 static int check_write_db_action(struct db_record *rec, void *check_ctx)
1064 {
1065         struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1066         TDB_DATA rec_val = dbwrap_record_get_value(rec);
1067         struct regkey *key = *(struct regkey**)rec_val.dptr;
1068         TALLOC_CTX *frame = talloc_stackframe();
1069
1070         /* write subkeylist */
1071         if ((ctx->version > 2) || (key->nsubkeys > 0) || (key->has_subkeylist)) {
1072                 write_subkeylist(ctx->odb, key, ctx->sep);
1073         }
1074
1075         /* write sorted subkeys */
1076         if ((ctx->version < 3) && (key->nsubkeys > 0)) {
1077                 write_sorted(ctx->odb, key, ctx->sep);
1078         }
1079
1080         /* write value list */
1081         if (key->nvalues > 0) {
1082                 write_values(ctx->odb, key, ctx->sep);
1083         }
1084
1085         /* write sd */
1086         if (key->sd) {
1087                 write_sd(ctx->odb, key, ctx->sep);
1088         }
1089
1090         talloc_free(frame);
1091         return 0;
1092 }
1093
1094 static int fix_tree_action(struct db_record *rec, void *check_ctx)
1095 {
1096         struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1097         TDB_DATA rec_key = dbwrap_record_get_key(rec);
1098         TDB_DATA rec_val = dbwrap_record_get_value(rec);
1099         struct regkey* key = *(struct regkey**)rec_val.dptr;
1100         if (ctx->opt.verbose) {
1101                 printf("Check Tree: %s\n", key->path);
1102         }
1103
1104         assert (strncmp(key->path, (char*)rec_key.dptr, rec_key.dsize) == 0);
1105
1106         /* assert(dbwrap_exists(ctx->db, string_term_tdb_data(key->path)) */
1107         /*        == key->exists); */
1108
1109         if (key->needs_update) {
1110                 printf("Update key: \"%s\"\n", key->path);
1111                 if ((ctx->version > 2) || (key->nsubkeys > 0)) {
1112                         write_subkeylist(ctx->odb, key, ctx->sep);
1113                 }
1114                 if ((ctx->version <= 2) && (key->nsubkeys > 0)) {
1115                         write_sorted(ctx->odb, key, ctx->sep);
1116                 }
1117                 if (key->nvalues > 0) {
1118                         write_values(ctx->odb, key, ctx->sep);
1119                 }
1120                 if (key->sd) {
1121                         write_sd(ctx->odb, key, ctx->sep);
1122                 }
1123         } else if (!key->has_subkeylist) {
1124                 if ((ctx->version > 2) || (key->nsubkeys > 0)) {
1125                         printf("Missing subkeylist: %s\n", key->path);
1126                         write_subkeylist(ctx->odb, key, ctx->sep);
1127                 }
1128         }
1129
1130         if (key->name == NULL && key->parent->has_subkeylist) {
1131                 printf("Key not referenced by the its parents subkeylist: %s\n",
1132                        key->path);
1133                 write_subkeylist(ctx->odb, key->parent, ctx->sep);
1134         }
1135
1136 /* XXX check that upcase(name) matches last part of path ??? */
1137
1138         return 0;
1139 }
1140
1141
1142 /* give the same warnings as fix_tree_action */
1143 static int check_tree_action(struct db_record *rec, void *check_ctx)
1144 {
1145         struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1146         TDB_DATA rec_key = dbwrap_record_get_key(rec);
1147         TDB_DATA rec_val = dbwrap_record_get_value(rec);
1148         struct regkey* key = *(struct regkey**)rec_val.dptr;
1149         if (ctx->opt.verbose) {
1150                 printf("Check Tree: %s\n", key->path);
1151         }
1152
1153         assert (strncmp(key->path, (char*)rec_key.dptr, rec_key.dsize) == 0);
1154
1155         if (!key->has_subkeylist) {
1156                 if ((ctx->version > 2) || (key->nsubkeys > 0)) {
1157                         printf("Missing subkeylist: %s\n", key->path);
1158                 }
1159         }
1160
1161         if (key->name == NULL && key->parent->has_subkeylist) {
1162                 printf("Key not referenced by the its parents subkeylist: %s\n",
1163                        key->path);
1164         }
1165
1166         return 0;
1167 }
1168
1169 static int delete_invalid_action(struct db_record *rec, void* check_ctx)
1170 {
1171         NTSTATUS status;
1172         struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1173         TDB_DATA rec_key = dbwrap_record_get_key(rec);
1174         TDB_DATA rec_val = dbwrap_record_get_value(rec);
1175
1176
1177         printf("Delete key: \"%.*s\"",(int)rec_key.dsize, rec_key.dptr);
1178         if (rec_val.dsize > 0) {
1179                 printf(" in favour of \"%s\"\n", rec_val.dptr);
1180         } else {
1181                 putc('\n', stdout);
1182         }
1183
1184         status = dbwrap_delete(ctx->odb, rec_key);
1185         if (!NT_STATUS_IS_OK(status)) {
1186                 d_printf("delete key \"%.*s\" failed!\n",
1187                          (int)rec_key.dsize, rec_key.dptr);
1188                 return -1;
1189         }
1190         return 0;
1191 }
1192
1193 static bool check_ctx_check_tree(struct check_ctx *ctx) {
1194         NTSTATUS status;
1195
1196         status = dbwrap_traverse(ctx->reg, check_tree_action, ctx, NULL);
1197         if (!NT_STATUS_IS_OK(status)) {
1198                 DEBUG(0, ("check traverse failed: %s\n",
1199                           nt_errstr(status)));
1200                 return false;
1201         }
1202         return true;
1203 }
1204 static bool check_ctx_fix_inplace(struct check_ctx *ctx) {
1205         NTSTATUS status;
1206         status = dbwrap_traverse(ctx->reg, fix_tree_action, ctx, NULL);
1207         if (!NT_STATUS_IS_OK(status)) {
1208                 DEBUG(0, ("fix traverse failed: %s\n",
1209                           nt_errstr(status)));
1210                 return false;
1211         }
1212
1213         status = dbwrap_traverse(ctx->del, delete_invalid_action, ctx, NULL);
1214         if (!NT_STATUS_IS_OK(status)) {
1215                 DEBUG(0, ("delete traverse failed: %s\n",
1216                           nt_errstr(status)));
1217                 return false;
1218         }
1219         return true;
1220 }
1221
1222 static bool check_ctx_write_new_db(struct check_ctx *ctx) {
1223         NTSTATUS status;
1224
1225         assert(ctx->odb);
1226
1227         if (ctx->opt.wipe) {
1228                 int ret = dbwrap_wipe(ctx->odb);
1229                 if (ret != 0) {
1230                         DEBUG(0, ("wiping %s failed\n",
1231                                   ctx->opt.output));
1232                         return false;
1233                 }
1234         }
1235
1236         status = dbwrap_traverse(ctx->reg, check_write_db_action, ctx, NULL);
1237         if (!NT_STATUS_IS_OK(status)) {
1238                 DEBUG(0, ("traverse2 failed: %s\n", nt_errstr(status)));
1239                 return false;
1240         }
1241
1242         status = dbwrap_store_uint32(ctx->odb,
1243                                      "INFO/version", ctx->version);
1244         if (!NT_STATUS_IS_OK(status)) {
1245                 DEBUG(0, ("write version failed: %s\n", nt_errstr(status)));
1246                 return false;
1247         }
1248         return true;
1249 }
1250
1251 int net_registry_check_db(const char *name, const struct check_options *opt)
1252 {
1253         NTSTATUS status;
1254         int ret = -1;
1255         struct check_ctx *ctx = check_ctx_create(talloc_tos(), name, opt);
1256         if (ctx==NULL) {
1257                 goto done;
1258         }
1259
1260         d_printf("Check database: %s\n", name);
1261
1262         /* 1. open output RW */
1263         if (!check_ctx_open_output(ctx)) {
1264                 goto done;
1265         }
1266
1267         /* 2. open input RO */
1268         if (!check_ctx_open_input(ctx)) {
1269                 goto done;
1270         }
1271
1272         if (opt->lock && !check_ctx_transaction_start(ctx)) {
1273                 goto done;
1274         }
1275
1276         if (!get_version(ctx)) {
1277                 goto done;
1278         }
1279
1280         status = dbwrap_traverse_read(ctx->idb, check_tdb_action, ctx, NULL);
1281         if (!NT_STATUS_IS_OK(status)) {
1282                 DEBUG(0, ("check traverse failed: %s\n", nt_errstr(status)));
1283                 goto done;
1284         }
1285
1286         if (!opt->lock && !check_ctx_transaction_start(ctx)) {
1287                 goto done;
1288         }
1289
1290         if (ctx->opt.repair && !ctx->opt.wipe) {
1291                 if (!check_ctx_fix_inplace(ctx)) {
1292                         goto done;
1293                 }
1294         } else {
1295                 if (!check_ctx_check_tree(ctx)) {
1296                         goto done;
1297                 }
1298                 if (ctx->odb) {
1299                         if (!check_ctx_write_new_db(ctx)) {
1300                                 goto done;
1301                         }
1302                 }
1303         }
1304         ret = 0;
1305 done:
1306         check_ctx_transaction_stop(ctx, ret == 0);
1307
1308         talloc_free(ctx);
1309         return ret;
1310 }
1311
1312 /*Local Variables:*/
1313 /*mode: c*/
1314 /*End:*/