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