s3-utils: net - Fix one error/usage message
[ira/wip.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 char* parent_path(const char *path, char sep)
257 {
258         const char *p = strrchr(path, sep);
259         return p ? talloc_strndup(talloc_tos(), path, p-path) : NULL;
260 }
261
262 /* return the regkey corresponding to path, create if not yet existing */
263 static struct regkey*
264 check_ctx_lookup_key(struct check_ctx *ctx, const char *path) {
265         struct regkey *ret = NULL;
266         NTSTATUS status;
267         TDB_DATA val;
268
269         if ( path == NULL) {
270                 return ctx->root;
271         }
272
273         status = dbwrap_fetch(ctx->reg, ctx, string_term_tdb_data(path), &val);
274         if (!NT_STATUS_IS_OK(status)) {
275                 return NULL;
276         }
277         if (val.dptr != NULL) {
278                 if (ctx->opt.verbose) {
279                         printf("Open: %s\n", path);
280                 }
281                 ret = *(struct regkey**)val.dptr;
282         } else {
283                 /* not yet existing, create */
284                 char *pp;
285                 if (ctx->opt.verbose) {
286                         printf("New: %s\n", path);
287                 }
288                 ret = talloc_zero(ctx, struct regkey);
289                 if (ret == NULL) {
290                         DEBUG(0, ("Out of memory!\n"));
291                         goto done;
292                 }
293                 ret->path = talloc_strdup(ret, path);
294
295                 pp = parent_path(path, ctx->sep);
296                 ret->parent = check_ctx_lookup_key(ctx, pp);
297                 regkey_add_subkey(ret->parent, ret);
298                 TALLOC_FREE(pp);
299
300                 /* the dummy root key has no subkeylist so set the name */
301                 if (ret->parent == ctx->root) {
302                         ret->name = talloc_strdup(ret, path);
303                 }
304
305                 dbwrap_store(ctx->reg, string_term_tdb_data(path),
306                              make_tdb_data((void*)&ret, sizeof(ret)), 0);
307         }
308 done:
309         talloc_free(val.dptr);
310         return ret;
311 }
312
313 static struct check_ctx* check_ctx_create(TALLOC_CTX *mem_ctx, const char *db,
314                                           const struct check_options *opt)
315 {
316         struct check_ctx *ctx = talloc_zero(mem_ctx, struct check_ctx);
317
318         ctx->opt = *opt;
319         ctx->reg = db_open_rbt(ctx);
320         ctx->del = db_open_rbt(ctx);
321         ctx->root = talloc_zero(ctx, struct regkey);
322         ctx->fname = talloc_strdup(ctx, db);
323
324         if (opt->automatic && (opt->output == NULL)) {
325                 ctx->opt.repair = true;
326                 ctx->opt.output = ctx->fname;
327         }
328
329         if (opt->repair) {
330                 if (opt->output) {
331                         d_fprintf(stderr, "You can not specify --output "
332                                   "with --repair\n");
333                         goto fail;
334                 } else {
335                         ctx->opt.output = ctx->fname;
336                 }
337         }
338
339         ctx->default_action = 'r';
340         return ctx;
341 fail:
342         talloc_free(ctx);
343         return NULL;
344 }
345
346 static bool check_ctx_open_output(struct check_ctx *ctx)
347 {
348         int oflags = O_RDWR | O_CREAT ;
349
350         if (ctx->opt.output == NULL) {
351                 return true;
352         }
353
354         if (!ctx->opt.repair) {
355                 if (!ctx->opt.wipe) {
356                         oflags |= O_EXCL;
357                 }
358                 ctx->opt.wipe = true;
359         }
360
361         ctx->odb = db_open(ctx, ctx->opt.output, 0, TDB_DEFAULT, oflags, 0644);
362         if (ctx->odb == NULL) {
363                 d_fprintf(stderr,
364                           _("Could not open db (%s) for writing: %s\n"),
365                           ctx->opt.output, strerror(errno));
366                 return false;
367         }
368         return true;
369 }
370
371
372 static bool check_ctx_open_input(struct check_ctx *ctx) {
373         ctx->idb = db_open(ctx, ctx->fname, 0, TDB_DEFAULT, O_RDONLY, 0);
374         if (ctx->idb == NULL) {
375                 d_fprintf(stderr,
376                           _("Could not open db (%s) for reading: %s\n"),
377                           ctx->fname, strerror(errno));
378                 return false;
379         }
380         return true;
381 }
382
383 static bool check_ctx_transaction_start(struct check_ctx *ctx) {
384         if (ctx->odb == NULL) {
385                 return true;
386         }
387         if (dbwrap_transaction_start(ctx->odb) != 0) {
388                 DEBUG(0, ("transaction_start failed\n"));
389                 return false;
390         }
391         ctx->transaction = true;
392         return true;
393 }
394
395 static void check_ctx_transaction_stop(struct check_ctx *ctx, bool ok) {
396         if (!ctx->transaction) {
397                 return;
398         }
399         if (!ctx->opt.test && ok) {
400                 d_printf("Commiting changes\n");
401                 if (dbwrap_transaction_commit(ctx->odb) != 0) {
402                         DEBUG(0, ("transaction_commit failed\n"));
403                 }
404         } else {
405                 d_printf("Discarding changes\n");
406                 dbwrap_transaction_cancel(ctx->odb);
407         }
408 }
409
410 static bool read_info(struct check_ctx *ctx, const char *key, TDB_DATA val)
411 {
412         if (val.dsize==sizeof(uint32_t) && strcmp(key, "version")==0) {
413                 uint32_t v = IVAL(val.dptr, 0);
414                 printf("INFO: %s = %d\n", key, v);
415                 return true;
416         }
417         printf("INFO: %s = <invalid>\n", key);
418         return false;
419 }
420
421 static bool is_all_upper(const char *str) {
422         bool ret;
423         char *tmp = talloc_strdup(talloc_tos(), str);
424         strupper_m(tmp);
425         ret = (strcmp(tmp, str) == 0);
426         talloc_free(tmp);
427         return ret;
428 }
429
430 static void move_to_back(struct regkey *key, struct regkey *subkey)
431 {
432         struct regkey **ptr;
433         size_t nidx;
434
435         DEBUG(5, ("Move to back subkey \"%s\" of \"%s\"\n",
436                   subkey->path, key->path));
437
438         for (ptr=key->subkeys; *ptr != subkey; ptr++)
439                 ;
440
441         nidx = ptr + 1 - key->subkeys;
442         memmove(ptr, ptr+1, (key->nsubkeys - nidx) * sizeof(*ptr));
443
444         key->subkeys[key->nsubkeys-1] = subkey;
445 }
446
447 static void set_subkey_name(struct check_ctx *ctx, struct regkey *key,
448                             const char *name, int nlen)
449 {
450         char *path = key->path;
451         TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
452         char *p;
453         struct regkey *subkey;
454         char *nname = talloc_strndup(mem_ctx, name, nlen);
455         remove_all(nname, ctx->sep);
456
457         if (strncmp(name, nname, nlen) != 0) {
458                 /* XXX interaction: delete/edit */
459                 printf("Warning: invalid name: \"%s\" replace with \"%s\"\n",
460                        name, nname);
461                 key->needs_update = true;
462         }
463         p = talloc_asprintf_strupper_m(mem_ctx, "%s%c%s",
464                                        path, ctx->sep, nname);
465         subkey = check_ctx_lookup_key(ctx, p);
466         if (subkey->name) {
467                 bool do_replace = false;
468
469                 if (strcmp(subkey->name, nname) != 0) {
470                         int action;
471                         char default_action;
472
473                         if (is_all_upper(nname)) {
474                                 default_action = 'o';
475                         } else {
476                                 default_action = 'n';
477                         }
478
479                         printf("Conflicting subkey names of [%s]: "
480                                "old: \"%s\", new: \"%s\"\n",
481                                key->path, subkey->name, nname);
482
483                         if (ctx->opt.output == NULL || ctx->opt.automatic) {
484                                 action = default_action;
485                         } else {
486                                 do {
487                                         action = interact_prompt(
488                                                 "choose spelling [o]ld, [n]ew,"
489                                                 "or [e]dit", "one",
490                                                 default_action);
491                                         if (action == 'e') {
492                                                 printf("Sorry, edit is not yet "
493                                                        "implemented here...\n");
494                                         }
495                                 } while (action == 'e');
496                         }
497
498                         if (action == 'n') {
499                                 do_replace = true;
500                         }
501                 }
502
503                 if (do_replace) {
504                         if (ctx->opt.verbose) {
505                                 printf("Replacing name: %s: \"%s\""
506                                        " -> \"%s\"\n", path,
507                                        subkey->name, nname);
508                         }
509                         TALLOC_FREE(subkey->name);
510                         subkey->name = talloc_steal(subkey, nname);
511                         key->needs_update = true;
512                 }
513         } else {
514                 if (ctx->opt.verbose) {
515                         printf("Set name: %s: \"%s\"\n", path, nname);
516                 }
517                 subkey->name = talloc_steal(subkey, nname);
518         }
519
520         move_to_back(key, subkey);
521         TALLOC_FREE(mem_ctx);
522 }
523
524 static void
525 read_subkeys(struct check_ctx *ctx, const char *path, TDB_DATA val, bool update)
526 {
527         uint32_t num_items, found_items = 0;
528         char *subkey;
529         struct regkey *key = check_ctx_lookup_key(ctx, path);
530
531         key->needs_update |= update;
532
533         /* printf("SUBKEYS: %s\n", path); */
534         if (key->has_subkeylist) {
535                 printf("Duplicate subkeylist \"%s\"\n",
536                        path);
537                 found_items = key->nsubkeys;
538         }
539
540         /* exists as defined by regdb_key_exists() */
541         key->has_subkeylist = true;
542
543         /* name is set if a key is referenced by the */
544         /* subkeylist of its parent. */
545
546         if (!tdb_data_read_uint32(&val, &num_items) ) {
547                 printf("Invalid subkeylist: \"%s\"\n", path);
548                 return;
549         }
550
551         while (tdb_data_read_cstr(&val, &subkey)) {
552                 /* printf(" SUBKEY: %s\n", subkey); */
553                 set_subkey_name(ctx, key, subkey, strlen(subkey));
554                 found_items++;
555         }
556
557         if (val.dsize != 0) {
558                 printf("Subkeylist of \"%s\": trailing: \"%.*s\"\n",
559                        path, (int)val.dsize, val.dptr);
560                 /* ask: best effort, delete or edit?*/
561                 set_subkey_name(ctx, key, (char*)val.dptr, val.dsize);
562                 found_items++;
563                 key->needs_update = true;
564         }
565
566         if (num_items != found_items) {
567                 printf("Subkeylist of \"%s\": invalid number of subkeys, "
568                        "expected: %d got: %d\n", path, num_items, found_items);
569                 key->needs_update = true;
570         }
571
572 }
573
574 static void read_values(struct check_ctx *ctx, const char *path, TDB_DATA val)
575 {
576         struct regkey *key = check_ctx_lookup_key(ctx, path);
577         uint32_t num_items, found_items;
578         struct regval value;
579
580         /* printf("VALUES: %s\n", path); */
581
582         if (!tdb_data_read_uint32(&val, &num_items) ) {
583                 printf("Invalid valuelist: \"%s\"\n", path);
584                 return;
585         }
586
587         found_items=0;
588         while (tdb_data_read_regval(&val, &value)) {
589                 /* printf(" VAL: %s type: %s(%d) length: %d\n", value.name, */
590                 /*        str_regtype(value.type), value.type, */
591                 /*        (int)value.data.length); */
592                 regkey_add_regval(key, regval_copy(key, &value));
593                 found_items++;
594         }
595
596         if (num_items != found_items) {
597                 printf("Valuelist of \"%s\": invalid number of values, "
598                        "expected: %d got: %d\n", path, num_items, found_items);
599                 key->needs_update = true;
600         }
601
602         if (val.dsize != 0) {
603                 printf("Valuelist of \"%s\": trailing: \"%*s\"\n", path,
604                        (int)val.dsize, val.dptr);
605                 key->needs_update = true;
606                 /* XXX best effort ??? */
607                 /* ZERO_STRUCT(value); */
608                 /* if (tdb_data_read_cstr(&val, &value.name) */
609                 /*     && tdb_data_read_uint32(&val, &value.type)) */
610                 /* { */
611                 /*      uint32_t len = -1; */
612                 /*      tdb_data_read_uint32(&val, &len); */
613                 /*      ... */
614                 /*      found_items ++; */
615                 /*      regkey_add_regval(key, regval_copy(key, value)); */
616                 /* } */
617         }
618         if (found_items == 0) {
619                 printf("Valuelist of \"%s\" empty\n", path);
620                 key->needs_update = true;
621         }
622 }
623
624 static bool read_sorted(struct check_ctx *ctx, const char *path, TDB_DATA val)
625 {
626         if (ctx->version >= 3) {
627                 return false;
628         }
629
630         if ((val.dptr == NULL) || (val.dsize<4)) {
631                 return false;
632         }
633
634         /* ToDo: check */
635         /* struct regkey *key = check_ctx_lookup_key(ctx, path); */
636         /* printf("SORTED: %s\n", path); */
637         return true;
638 }
639
640 static bool read_sd(struct check_ctx *ctx, const char *path, TDB_DATA val)
641 {
642         NTSTATUS status;
643         struct regkey *key = check_ctx_lookup_key(ctx, path);
644         /* printf("SD: %s\n", path); */
645
646         status = unmarshall_sec_desc(key, val.dptr, val.dsize, &key->sd);
647         if (!NT_STATUS_IS_OK(status)) {
648                 DEBUG(0, ("Failed to read SD of %s: %s\n",
649                           path, nt_errstr(status)));
650         }
651         return true;
652 }
653
654 static bool srprs_path(const char **ptr, const char* prefix, char sep,
655                        const char **ppath)
656 {
657         const char *path, *pos = *ptr;
658         if (prefix != NULL) {
659                 if (!srprs_str(&pos, prefix, -1) || !srprs_char(&pos, sep) ) {
660                         return false;
661                 }
662         }
663         path = pos;
664         if ( !srprs_hive(&pos, NULL) ) {
665                 return false;
666         }
667         if ( !srprs_eos(&pos) && !srprs_char(&pos, sep) ) {
668                 return false;
669         }
670         *ppath = path;
671         *ptr = rawmemchr(pos, '\0');
672         return true;
673 }
674
675 /* Fixme: this dosn't work in the general multibyte char case.
676    see string_replace()
677 */
678 static bool normalize_path_internal(char* path, char sep) {
679         size_t len = strlen(path);
680         const char *orig = talloc_strndup(talloc_tos(), path, len);
681         char *optr = path, *iptr = path;
682         bool changed;
683
684         while (*iptr == sep ) {
685                 iptr++;
686         }
687         while (*iptr) {
688                 *optr = *iptr;
689                 if (*iptr == sep) {
690                         while (*iptr == sep) {
691                                 iptr++;
692                         }
693                         if (*iptr) {
694                                 optr++;
695                         }
696                 } else {
697                         iptr++;
698                         optr++;
699                 }
700         }
701         *optr = '\0';
702
703         strupper_m(path);
704         changed = (strcmp(orig, path) != 0);
705         talloc_free(discard_const(orig));
706         return changed;
707 }
708
709 static bool normalize_path(char* path, char sep) {
710         static const char* SEPS = "\\/";
711         char* firstsep = strpbrk(path, SEPS);
712         bool wrong_sep = (firstsep && (*firstsep != sep));
713
714         assert (strchr(SEPS, sep));
715
716         if (wrong_sep) {
717                 string_replace(path, *firstsep, sep);
718         }
719         return normalize_path_internal(path, sep) || wrong_sep;
720 }
721
722 static int check_tdb_action(struct db_record *rec, void *check_ctx)
723 {
724         struct check_ctx *ctx = (struct check_ctx*)check_ctx;
725         TALLOC_CTX *frame = talloc_stackframe();
726         TDB_DATA val = dbwrap_record_get_value(rec);
727         TDB_DATA rec_key = dbwrap_record_get_key(rec);
728         char *key;
729         bool invalid_path = false;
730         bool once_more;
731         bool first_iter = true;
732
733         if (!tdb_data_is_cstr(rec_key)) {
734                 printf("Key is not zero terminated: \"%.*s\"\ntry to go on.\n",
735                        (int)rec_key.dsize, rec_key.dptr);
736                 invalid_path = true;
737         }
738         key = talloc_strndup(frame, (char*)rec_key.dptr, rec_key.dsize);
739
740         do {
741                 const char *path, *pos = key;
742                 once_more = false;
743
744                 if (srprs_str(&pos, "INFO/", -1)) {
745                         if ( read_info(ctx, pos, val) ) {
746                                 break;
747                         }
748                         invalid_path = true;
749                         /* ask: mark invalid */
750                 } else if (srprs_str(&pos, "__db_sequence_number__", -1)) {
751                         printf("Skip key: \"%.*s\"\n",
752                                (int)rec_key.dsize, rec_key.dptr);
753                         /* skip: do nothing + break */
754                         break;
755
756                 } else if (normalize_path(key, ctx->sep)) {
757                         printf("Unnormal key: \"%.*s\"\n",
758                                (int)rec_key.dsize, rec_key.dptr);
759                         printf("Normalize to: \"%s\"\n", key);
760                         invalid_path = true;
761                 } else if (srprs_path(&pos, NULL,
762                                       ctx->sep, &path))
763                 {
764                         read_subkeys(ctx, path, val, invalid_path);
765                         break;
766                 } else if (srprs_path(&pos, REG_VALUE_PREFIX,
767                                       ctx->sep, &path))
768                 {
769                         read_values(ctx, path, val);
770                         break;
771                 } else if (srprs_path(&pos, REG_SECDESC_PREFIX,
772                                       ctx->sep, &path))
773                 {
774                         read_sd(ctx, path, val);
775                         break;
776                 } else if (srprs_path(&pos, REG_SORTED_SUBKEYS_PREFIX,
777                                       ctx->sep, &path))
778                 {
779                         if (!read_sorted(ctx, path, val)) {
780                                 /* delete: mark invalid + break */
781                                 printf("Invalid sorted subkeys for: \"%s\"\n", path);
782                                 invalid_path = true;
783                                 key = NULL;
784                         }
785                         break;
786                 } else {
787                         printf("Unrecognized key: \"%.*s\"\n",
788                                (int)rec_key.dsize, rec_key.dptr);
789                         invalid_path = true;
790                 }
791
792                 if (invalid_path) {
793                         int action;
794                         if (ctx->opt.output == NULL) {
795                                 action = first_iter ? 'r' : 's';
796                         } else if (ctx->opt.automatic) {
797                                 action = first_iter ? 'r' : 'd';
798                         } else if (ctx->auto_action != '\0') {
799                                 action = ctx->auto_action;
800                         } else {
801                                 action = interact_prompt("[s]kip,[S]kip all,"
802                                                          "[d]elete,[D]elete all"
803                                                          ",[e]dit,[r]etry"
804                                                          , "sder",
805                                                          ctx->default_action);
806                         }
807                         if (isupper(action)) {
808                                 action = tolower(action);
809                                 ctx->auto_action = action;
810                         }
811                         ctx->default_action = action;
812                         switch (action) {
813                         case 's': /* skip */
814                                 invalid_path = false;
815                                 break;
816                         case 'd': /* delete */
817                                 invalid_path = true;
818                                 key = NULL;
819                                 break;
820                         case 'e': /* edit */ {
821                                 char *p = interact_edit(frame, key);
822                                 if (p) {
823                                         talloc_free(key);
824                                         key = p;
825                                 }
826                         } /* fall through */
827                         case 'r': /* retry */
828                                 once_more = true;
829                                 break;
830                         }
831                 }
832                 first_iter = false;
833         } while (once_more);
834
835         if (invalid_path) {
836                 dbwrap_store(ctx->del, rec_key, string_term_tdb_data(key), 0);
837         }
838
839         talloc_free(frame);
840         return 0;
841 }
842
843 static bool get_version(struct check_ctx *ctx) {
844         static const uint32_t curr_version = REGDB_CODE_VERSION;
845         uint32_t version = ctx->opt.version ? ctx->opt.version : curr_version;
846         uint32_t info_version = 0;
847         NTSTATUS status;
848
849         status = dbwrap_fetch_uint32(ctx->idb, "INFO/version", &info_version);
850         if (!NT_STATUS_IS_OK(status)) {
851                 printf("Warning: no INFO/version found!\n");
852                 /* info_version = guess_version(ctx); */
853         }
854
855         if (ctx->opt.version) {
856                 version = ctx->opt.version;
857         } else if (ctx->opt.implicit_db) {
858                 version = curr_version;
859         } else {
860                 version = info_version;
861         }
862
863         if (!version) {
864                 printf("Couldn't determine registry format version, "
865                        "specify with --reg-version\n");
866                 return false;
867         }
868
869
870         if ( version != info_version ) {
871                 if (ctx->opt.force || !ctx->opt.repair) {
872                         printf("Warning: overwrite registry format "
873                                "version %d with %d\n", info_version, version);
874                 } else {
875                         printf("Warning: found registry format version %d but "
876                                "expected %d\n", info_version, version);
877                         return false;
878                 }
879         }
880
881         ctx->version = version;
882         ctx->sep = (version > 1) ? '\\' : '/';
883
884         return true;
885 }
886
887 static bool
888 dbwrap_store_verbose(struct db_context *db, const char *key, TDB_DATA nval)
889 {
890         TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
891         TDB_DATA oval;
892         NTSTATUS status;
893
894         status = dbwrap_fetch_bystring(db, mem_ctx, key, &oval);
895         if (!NT_STATUS_IS_OK(status)) {
896                 printf ("store %s failed to fetch old value: %s\n", key,
897                         nt_errstr(status));
898                 goto done;
899         }
900
901         if (!tdb_data_is_empty(oval) && !tdb_data_equal(nval, oval)) {
902                 printf("store %s:\n"
903                        "  overwrite: %s\n"
904                        "  with:      %s\n",
905                        key, tdb_data_print(mem_ctx, oval),
906                        tdb_data_print(mem_ctx, nval));
907         }
908
909         status = dbwrap_store_bystring(db, key, nval, 0);
910         if (!NT_STATUS_IS_OK(status)) {
911                 printf ("store %s failed: %s\n", key, nt_errstr(status));
912         }
913
914 done:
915         talloc_free(mem_ctx);
916         return NT_STATUS_IS_OK(status);
917 }
918
919
920 static int cmp_keynames(char **p1, char **p2)
921 {
922         return strcasecmp_m(*p1, *p2);
923 }
924
925 static bool
926 write_subkeylist(struct db_context *db, struct regkey *key, char sep)
927 {
928         cbuf *buf = cbuf_new(talloc_tos());
929         int i;
930         bool ret;
931
932         cbuf_putdw(buf, key->nsubkeys);
933
934         for (i=0; i < key->nsubkeys; i++) {
935                 struct regkey *subkey = key->subkeys[i];
936                 const char *name = subkey->name;
937                 if (name == NULL) {
938                         printf("Warning: no explicite name for key %s\n",
939                                subkey->path);
940                         name = strrchr_m(subkey->path, sep);
941                         assert(name);
942                         name ++;
943                 }
944                 cbuf_puts(buf, name, -1);
945                 cbuf_putc(buf, '\0');
946         }
947
948         ret = dbwrap_store_verbose(db, key->path, cbuf_make_tdb_data(buf));
949
950         talloc_free(buf);
951         return ret;
952 }
953
954 static bool write_sorted(struct db_context *db, struct regkey *key, char sep)
955 {
956         cbuf *buf = cbuf_new(talloc_tos());
957         char *path;
958         int i;
959         bool ret = false;
960         char **sorted = talloc_zero_array(buf, char*, key->nsubkeys);
961         int offset =  (1 + key->nsubkeys) * sizeof(uint32_t);
962
963         for (i=0; i < key->nsubkeys; i++) {
964                 sorted[i] = talloc_strdup_upper(sorted, key->subkeys[i]->name);
965         }
966         TYPESAFE_QSORT(sorted, key->nsubkeys, cmp_keynames);
967
968         cbuf_putdw(buf, key->nsubkeys);
969         for (i=0; i < key->nsubkeys; i++) {
970                 cbuf_putdw(buf, offset);
971                 offset += strlen(sorted[i]) + 1;
972         }
973         for (i=0; i < key->nsubkeys; i++) {
974                 cbuf_puts(buf, sorted[i], -1);
975                 cbuf_putc(buf, '\0');
976         }
977
978         path = talloc_asprintf(buf, "%s%c%s", REG_SORTED_SUBKEYS_PREFIX, sep,
979                                key->path);
980         if (path == NULL) {
981                 DEBUG(0, ("Out of memory!\n"));
982                 goto done;
983         }
984
985         ret = dbwrap_store_verbose(db, path, cbuf_make_tdb_data(buf));
986 done:
987         talloc_free(buf);
988         return ret;
989 }
990
991 static bool write_values(struct db_context *db, struct regkey *key, char sep)
992 {
993         cbuf *buf = cbuf_new(talloc_tos());
994         char *path;
995         int i;
996         bool ret = false;
997
998         cbuf_putdw(buf, key->nvalues);
999         for (i=0; i < key->nvalues; i++) {
1000                 struct regval *val = key->values[i];
1001                 cbuf_puts(buf, val->name, -1);
1002                 cbuf_putc(buf, '\0');
1003                 cbuf_putdw(buf, val->type);
1004                 cbuf_putdw(buf, val->data.length);
1005                 cbuf_puts(buf, (void*)val->data.data, val->data.length);
1006         }
1007
1008         path = talloc_asprintf(buf, "%s%c%s", REG_VALUE_PREFIX, sep, key->path);
1009         if (path == NULL) {
1010                 DEBUG(0, ("Out of memory!\n"));
1011                 goto done;
1012         }
1013
1014         ret = dbwrap_store_verbose(db, path, cbuf_make_tdb_data(buf));
1015 done:
1016         talloc_free(buf);
1017         return ret;
1018 }
1019
1020 static bool write_sd(struct db_context *db, struct regkey *key, char sep)
1021 {
1022         TDB_DATA sd;
1023         NTSTATUS status;
1024         char *path;
1025         bool ret = false;
1026         TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
1027
1028         status = marshall_sec_desc(mem_ctx, key->sd, &sd.dptr, &sd.dsize);
1029         if (!NT_STATUS_IS_OK(status)) {
1030                 printf("marshall sec desc %s failed: %s\n",
1031                        key->path, nt_errstr(status));
1032                 goto done;
1033         }
1034         path = talloc_asprintf(mem_ctx, "%s%c%s", REG_SECDESC_PREFIX,
1035                                sep, key->path);
1036         if (path == NULL) {
1037                 DEBUG(0, ("Out of memory!\n"));
1038                 goto done;
1039         }
1040
1041         ret = dbwrap_store_verbose(db, path, sd);
1042 done:
1043         talloc_free(mem_ctx);
1044         return ret;
1045 }
1046
1047
1048 static int check_write_db_action(struct db_record *rec, void *check_ctx)
1049 {
1050         struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1051         TDB_DATA rec_val = dbwrap_record_get_value(rec);
1052         struct regkey *key = *(struct regkey**)rec_val.dptr;
1053         TALLOC_CTX *frame = talloc_stackframe();
1054
1055         /* write subkeylist */
1056         if ((ctx->version > 2) || (key->nsubkeys > 0) || (key->has_subkeylist)) {
1057                 write_subkeylist(ctx->odb, key, ctx->sep);
1058         }
1059
1060         /* write sorted subkeys */
1061         if ((ctx->version < 3) && (key->nsubkeys > 0)) {
1062                 write_sorted(ctx->odb, key, ctx->sep);
1063         }
1064
1065         /* write value list */
1066         if (key->nvalues > 0) {
1067                 write_values(ctx->odb, key, ctx->sep);
1068         }
1069
1070         /* write sd */
1071         if (key->sd) {
1072                 write_sd(ctx->odb, key, ctx->sep);
1073         }
1074
1075         talloc_free(frame);
1076         return 0;
1077 }
1078
1079 static int fix_tree_action(struct db_record *rec, void *check_ctx)
1080 {
1081         struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1082         TDB_DATA rec_key = dbwrap_record_get_key(rec);
1083         TDB_DATA rec_val = dbwrap_record_get_value(rec);
1084         struct regkey* key = *(struct regkey**)rec_val.dptr;
1085         if (ctx->opt.verbose) {
1086                 printf("Check Tree: %s\n", key->path);
1087         }
1088
1089         assert (strncmp(key->path, (char*)rec_key.dptr, rec_key.dsize) == 0);
1090
1091         /* assert(dbwrap_exists(ctx->db, string_term_tdb_data(key->path)) */
1092         /*        == key->exists); */
1093
1094         if (key->needs_update) {
1095                 printf("Update key: \"%s\"\n", key->path);
1096                 if ((ctx->version > 2) || (key->nsubkeys > 0)) {
1097                         write_subkeylist(ctx->odb, key, ctx->sep);
1098                 }
1099                 if ((ctx->version <= 2) && (key->nsubkeys > 0)) {
1100                         write_sorted(ctx->odb, key, ctx->sep);
1101                 }
1102                 if (key->nvalues > 0) {
1103                         write_values(ctx->odb, key, ctx->sep);
1104                 }
1105                 if (key->sd) {
1106                         write_sd(ctx->odb, key, ctx->sep);
1107                 }
1108         } else if (!key->has_subkeylist) {
1109                 if ((ctx->version > 2) || (key->nsubkeys > 0)) {
1110                         printf("Missing subkeylist: %s\n", key->path);
1111                         write_subkeylist(ctx->odb, key, ctx->sep);
1112                 }
1113         }
1114
1115         if (key->name == NULL && key->parent->has_subkeylist) {
1116                 printf("Key not referenced by the its parents subkeylist: %s\n",
1117                        key->path);
1118                 write_subkeylist(ctx->odb, key->parent, ctx->sep);
1119         }
1120
1121 /* XXX check that upcase(name) matches last part of path ??? */
1122
1123         return 0;
1124 }
1125
1126
1127 /* give the same warnings as fix_tree_action */
1128 static int check_tree_action(struct db_record *rec, void *check_ctx)
1129 {
1130         struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1131         TDB_DATA rec_key = dbwrap_record_get_key(rec);
1132         TDB_DATA rec_val = dbwrap_record_get_value(rec);
1133         struct regkey* key = *(struct regkey**)rec_val.dptr;
1134         if (ctx->opt.verbose) {
1135                 printf("Check Tree: %s\n", key->path);
1136         }
1137
1138         assert (strncmp(key->path, (char*)rec_key.dptr, rec_key.dsize) == 0);
1139
1140         if (!key->has_subkeylist) {
1141                 if ((ctx->version > 2) || (key->nsubkeys > 0)) {
1142                         printf("Missing subkeylist: %s\n", key->path);
1143                 }
1144         }
1145
1146         if (key->name == NULL && key->parent->has_subkeylist) {
1147                 printf("Key not referenced by the its parents subkeylist: %s\n",
1148                        key->path);
1149         }
1150
1151         return 0;
1152 }
1153
1154 static int delete_invalid_action(struct db_record *rec, void* check_ctx)
1155 {
1156         NTSTATUS status;
1157         struct check_ctx *ctx = (struct check_ctx*)check_ctx;
1158         TDB_DATA rec_key = dbwrap_record_get_key(rec);
1159         TDB_DATA rec_val = dbwrap_record_get_value(rec);
1160
1161
1162         printf("Delete key: \"%.*s\"",(int)rec_key.dsize, rec_key.dptr);
1163         if (rec_val.dsize > 0) {
1164                 printf(" in favour of \"%s\"\n", rec_val.dptr);
1165         } else {
1166                 putc('\n', stdout);
1167         }
1168
1169         status = dbwrap_delete(ctx->odb, rec_key);
1170         if (!NT_STATUS_IS_OK(status)) {
1171                 d_printf("delete key \"%.*s\" failed!\n",
1172                          (int)rec_key.dsize, rec_key.dptr);
1173                 return -1;
1174         }
1175         return 0;
1176 }
1177
1178 static bool check_ctx_check_tree(struct check_ctx *ctx) {
1179         NTSTATUS status;
1180
1181         status = dbwrap_traverse(ctx->reg, check_tree_action, ctx, NULL);
1182         if (!NT_STATUS_IS_OK(status)) {
1183                 DEBUG(0, ("check traverse failed: %s\n",
1184                           nt_errstr(status)));
1185                 return false;
1186         }
1187         return true;
1188 }
1189 static bool check_ctx_fix_inplace(struct check_ctx *ctx) {
1190         NTSTATUS status;
1191         status = dbwrap_traverse(ctx->reg, fix_tree_action, ctx, NULL);
1192         if (!NT_STATUS_IS_OK(status)) {
1193                 DEBUG(0, ("fix traverse failed: %s\n",
1194                           nt_errstr(status)));
1195                 return false;
1196         }
1197
1198         status = dbwrap_traverse(ctx->del, delete_invalid_action, ctx, NULL);
1199         if (!NT_STATUS_IS_OK(status)) {
1200                 DEBUG(0, ("delete traverse failed: %s\n",
1201                           nt_errstr(status)));
1202                 return false;
1203         }
1204         return true;
1205 }
1206
1207 static bool check_ctx_write_new_db(struct check_ctx *ctx) {
1208         NTSTATUS status;
1209
1210         assert(ctx->odb);
1211
1212         if (ctx->opt.wipe) {
1213                 int ret = dbwrap_wipe(ctx->odb);
1214                 if (ret != 0) {
1215                         DEBUG(0, ("wiping %s failed\n",
1216                                   ctx->opt.output));
1217                         return false;
1218                 }
1219         }
1220
1221         status = dbwrap_traverse(ctx->reg, check_write_db_action, ctx, NULL);
1222         if (!NT_STATUS_IS_OK(status)) {
1223                 DEBUG(0, ("traverse2 failed: %s\n", nt_errstr(status)));
1224                 return false;
1225         }
1226
1227         status = dbwrap_store_uint32(ctx->odb,
1228                                      "INFO/version", ctx->version);
1229         if (!NT_STATUS_IS_OK(status)) {
1230                 DEBUG(0, ("write version failed: %s\n", nt_errstr(status)));
1231                 return false;
1232         }
1233         return true;
1234 }
1235
1236 int net_registry_check_db(const char *name, const struct check_options *opt)
1237 {
1238         NTSTATUS status;
1239         int ret = -1;
1240         struct check_ctx *ctx = check_ctx_create(talloc_tos(), name, opt);
1241         if (ctx==NULL) {
1242                 goto done;
1243         }
1244
1245         d_printf("Check database: %s\n", name);
1246
1247         /* 1. open output RW */
1248         if (!check_ctx_open_output(ctx)) {
1249                 goto done;
1250         }
1251
1252         /* 2. open input RO */
1253         if (!check_ctx_open_input(ctx)) {
1254                 goto done;
1255         }
1256
1257         if (opt->lock && !check_ctx_transaction_start(ctx)) {
1258                 goto done;
1259         }
1260
1261         if (!get_version(ctx)) {
1262                 goto done;
1263         }
1264
1265         status = dbwrap_traverse_read(ctx->idb, check_tdb_action, ctx, NULL);
1266         if (!NT_STATUS_IS_OK(status)) {
1267                 DEBUG(0, ("check traverse failed: %s\n", nt_errstr(status)));
1268                 goto done;
1269         }
1270
1271         if (!opt->lock && !check_ctx_transaction_start(ctx)) {
1272                 goto done;
1273         }
1274
1275         if (ctx->opt.repair && !ctx->opt.wipe) {
1276                 if (!check_ctx_fix_inplace(ctx)) {
1277                         goto done;
1278                 }
1279         } else {
1280                 if (!check_ctx_check_tree(ctx)) {
1281                         goto done;
1282                 }
1283                 if (ctx->odb) {
1284                         if (!check_ctx_write_new_db(ctx)) {
1285                                 goto done;
1286                         }
1287                 }
1288         }
1289         ret = 0;
1290 done:
1291         check_ctx_transaction_stop(ctx, ret == 0);
1292
1293         talloc_free(ctx);
1294         return ret;
1295 }
1296
1297 /*Local Variables:*/
1298 /*mode: c*/
1299 /*End:*/