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