Line data Source code
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 idmap database.
22 : * @author Gregor Beck <gb@sernet.de>
23 : * @date Mar 2011
24 : */
25 :
26 : #include "net_idmap_check.h"
27 : #include "includes.h"
28 : #include "system/filesys.h"
29 : #include "dbwrap/dbwrap.h"
30 : #include "dbwrap/dbwrap_open.h"
31 : #include "dbwrap/dbwrap_rbt.h"
32 : #include "net.h"
33 : #include "../libcli/security/dom_sid.h"
34 : #include "cbuf.h"
35 : #include "srprs.h"
36 : #include "util_tdb.h"
37 : #include "interact.h"
38 :
39 : static int traverse_commit(struct db_record *diff_rec, void* data);
40 : static int traverse_check(struct db_record *rec, void* data);
41 :
42 : /* TDB_DATA *******************************************************************/
43 : static char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d);
44 : static TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr);
45 :
46 : /* record *********************************************************************/
47 :
48 : enum DT {
49 : DT_INV = 0,
50 : DT_SID, DT_UID, DT_GID,
51 : DT_HWM, DT_VER, DT_SEQ,
52 : };
53 :
54 : struct record {
55 : enum DT key_type, val_type;
56 : TDB_DATA key, val;
57 : struct dom_sid sid;
58 : long unsigned id;
59 : };
60 :
61 : static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val);
62 : static struct record* reverse_record(struct record* rec);
63 :
64 0 : static bool is_invalid(const struct record* r) {
65 0 : return (r->key_type == DT_INV) || (r->val_type == DT_INV);
66 : }
67 :
68 0 : static bool is_map(const struct record* r) {
69 0 : return (r->key_type == DT_SID)
70 0 : || (r->key_type == DT_UID) || (r->key_type == DT_GID);
71 : }
72 :
73 : /* action *********************************************************************/
74 :
75 : typedef struct check_action {
76 : void (*fmt)(struct check_action *a,
77 : struct record *r,
78 : TDB_DATA *v);
79 : const char* name;
80 : const char* prompt;
81 : const char* answers;
82 : char auto_action;
83 : char default_action;
84 : bool verbose;
85 : } check_action;
86 :
87 : struct check_actions {
88 : check_action invalid_record;
89 : check_action missing_reverse;
90 : check_action invalid_mapping;
91 : check_action invalid_edit;
92 : check_action record_exists;
93 : check_action no_version;
94 : check_action wrong_version;
95 : check_action invalid_hwm;
96 : check_action commit;
97 : check_action valid_mapping;
98 : check_action valid_other;
99 : check_action invalid_diff;
100 : };
101 :
102 0 : static void invalid_mapping_fmt(struct check_action *a,
103 : struct record *r,
104 : TDB_DATA *v)
105 : {
106 0 : d_printf("%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
107 : a->name,
108 : print_data(r, r->key),
109 : print_data(r, r->val),
110 0 : (v ? print_data(r, *v) : ""));
111 0 : }
112 :
113 0 : static void record_exists_fmt(struct check_action *a,
114 : struct record *r,
115 : TDB_DATA *v)
116 : {
117 0 : d_printf("%1$s: %2$s\n-%4$s\n+%3$s\n",
118 : a->name,
119 : print_data(r, r->key),
120 : print_data(r, r->val),
121 0 : (v ? print_data(r, *v) : ""));
122 0 : }
123 :
124 0 : static void valid_mapping_fmt(struct check_action *a,
125 : struct record *r,
126 : TDB_DATA *v)
127 : {
128 0 : d_printf("%1$s: %2$s <-> %3$s\n",
129 : a->name,
130 : print_data(r, r->key),
131 : print_data(r, r->val));
132 0 : }
133 :
134 : static struct check_actions
135 0 : check_actions_init(const struct check_options* opts) {
136 0 : struct check_actions ret = {
137 : .invalid_record = (check_action) {
138 : .name = "Invalid record",
139 : .prompt = "[e]dit/[d]elete/[D]elete all"
140 : "/[s]kip/[S]kip all",
141 : .answers = "eds",
142 : .default_action = 'e',
143 : .verbose = true,
144 : },
145 : .missing_reverse = (check_action) {
146 : .name = "Missing reverse mapping for",
147 : .prompt = "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all"
148 : "/[s]kip/[S]kip all",
149 : .answers = "feds",
150 : .default_action = 'f',
151 : .verbose = true,
152 : },
153 : .invalid_mapping = (check_action) {
154 : .fmt = invalid_mapping_fmt,
155 : .name = "Invalid mapping",
156 : .prompt = "[e]dit/[d]elete/[D]elete all"
157 : "/[s]kip/[S]kip all",
158 : .answers = "eds",
159 : .default_action = 'd',
160 : .verbose = true,
161 : },
162 : .invalid_edit = (check_action) {
163 : .name = "Invalid record",
164 : .prompt = "[e]dit/[d]elete/[D]elete all"
165 : "/[s]kip/[S]kip all",
166 : .answers = "eds",
167 : .default_action = 'e',
168 : .verbose = true,
169 : },
170 : .record_exists = (check_action) {
171 : .fmt = record_exists_fmt,
172 : .name = "Record exists",
173 : .prompt = "[o]verwrite/[O]verwrite all/[e]dit"
174 : "/[d]elete/[D]elete all/[s]kip/[S]kip all",
175 : .answers = "oeds",
176 : .default_action = 'o',
177 : .verbose = true,
178 : },
179 : .no_version = (check_action) {
180 : .prompt = "[f]ix/[s]kip/[a]bort",
181 : .answers = "fsa",
182 : .default_action = 'f',
183 : },
184 : .wrong_version = (check_action) {
185 : .prompt = "[f]ix/[s]kip/[a]bort",
186 : .answers = "fsa",
187 : .default_action = 'a',
188 : },
189 : .invalid_hwm = (check_action) {
190 : .prompt = "[f]ix/[s]kip",
191 : .answers = "fs",
192 : .default_action = 'f',
193 : },
194 : .commit = (check_action) {
195 : .prompt = "[c]ommit/[l]ist/[s]kip",
196 : .answers = "cls",
197 : .default_action = 'l',
198 : .verbose = true,
199 : },
200 : .valid_mapping = (check_action) {
201 : .fmt = valid_mapping_fmt,
202 : .name = "Mapping",
203 : .auto_action = 's',
204 0 : .verbose = opts->verbose,
205 : },
206 : .valid_other = (check_action) {
207 : .name = "Other",
208 : .auto_action = 's',
209 0 : .verbose = opts->verbose,
210 : },
211 : .invalid_diff = (check_action) {
212 : .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
213 : "/[a]bort",
214 : .answers = "sca",
215 : .default_action = 's',
216 : },
217 : };
218 :
219 0 : if (!opts->repair) {
220 0 : ret.invalid_record.auto_action = 's';
221 0 : ret.missing_reverse.auto_action = 's';
222 0 : ret.invalid_mapping.auto_action = 's';
223 0 : ret.no_version.auto_action = 's';
224 0 : ret.wrong_version.auto_action = 's';
225 0 : ret.invalid_hwm.auto_action = 's';
226 0 : ret.commit.auto_action = 's';
227 : }
228 :
229 0 : if (opts->automatic) {
230 0 : ret.invalid_record.auto_action = 'd'; /* delete */
231 0 : ret.missing_reverse.auto_action = 'f'; /* fix */
232 0 : ret.invalid_mapping.auto_action = 'd'; /* delete */
233 0 : ret.no_version.auto_action = 'f'; /* fix */
234 0 : ret.wrong_version.auto_action = 'a'; /* abort */
235 0 : ret.invalid_hwm.auto_action = 'f'; /* fix */
236 0 : ret.commit.auto_action = 'c'; /* commit */
237 0 : ret.invalid_diff.auto_action = 'a'; /* abort */
238 0 : if (opts->force) {
239 0 : ret.wrong_version.auto_action = 'f'; /* fix */
240 0 : ret.invalid_diff.auto_action = 'c'; /* commit */
241 : }
242 : }
243 0 : if (opts->test) {
244 0 : ret.invalid_diff.auto_action = 'c'; /* commit */
245 : /* ret.commit.auto_action = 'c';*/ /* commit */
246 : }
247 :
248 0 : return ret;
249 : }
250 :
251 0 : static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
252 0 : char ret;
253 0 : if (a->verbose && (r != NULL)) {
254 0 : if (!a->fmt) {
255 0 : d_printf("%s: %s ", a->name, print_data(r, r->key));
256 0 : if (is_map(r)) {
257 0 : d_printf("-> %s\n", print_data(r, r->val));
258 0 : } else if (r->key_type == DT_HWM ||
259 0 : r->key_type == DT_VER ||
260 0 : r->key_type == DT_SEQ)
261 : {
262 0 : d_printf(": %ld\n", r->id);
263 : } else {
264 0 : d_printf("\n");
265 : }
266 : } else {
267 0 : a->fmt(a, r, v);
268 : }
269 : }
270 :
271 0 : if (a->auto_action != '\0') {
272 0 : return a->auto_action;
273 : }
274 :
275 0 : ret = interact_prompt(a->prompt, a->answers, a->default_action);
276 :
277 0 : if (isupper(ret)) {
278 0 : ret = tolower(ret);
279 0 : a->auto_action = ret;
280 : }
281 0 : a->default_action = ret;
282 0 : return ret;
283 : }
284 :
285 : /* *************************************************************************/
286 :
287 : typedef struct {
288 : TDB_DATA oval, nval;
289 : } TDB_DATA_diff;
290 :
291 0 : static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
292 0 : return (TDB_DATA) {
293 : .dptr = (uint8_t *)diff,
294 : .dsize = sizeof(TDB_DATA_diff),
295 : };
296 : }
297 :
298 0 : static TDB_DATA_diff unpack_diff(TDB_DATA data) {
299 0 : assert(data.dsize == sizeof(TDB_DATA_diff));
300 0 : return *(TDB_DATA_diff*)data.dptr;
301 : }
302 :
303 : #define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \
304 : DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \
305 : if (!tdb_data_is_empty(OLD)) { \
306 : DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
307 : } \
308 : if (!tdb_data_is_empty(NEW)) { \
309 : DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
310 : }
311 :
312 : struct check_ctx {
313 : int oflags;
314 : char* name;
315 : bool transaction;
316 : struct db_context *db;
317 : struct db_context *diff;
318 : struct check_actions action;
319 :
320 : uint32_t uid_hwm;
321 : uint32_t gid_hwm;
322 :
323 : unsigned n_invalid_record;
324 : unsigned n_missing_reverse;
325 : unsigned n_invalid_mappping;
326 : unsigned n_map;
327 : unsigned n_other;
328 : unsigned n_diff;
329 : struct check_options opts;
330 : };
331 :
332 :
333 : static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
334 :
335 0 : static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
336 : {
337 0 : NTSTATUS status;
338 0 : TDB_DATA_diff diff;
339 0 : TALLOC_CTX* mem = talloc_new(ctx->diff);
340 0 : TDB_DATA recvalue;
341 0 : struct db_record *rec = dbwrap_fetch_locked(ctx->diff, mem, key);
342 :
343 0 : if (rec == NULL) {
344 0 : return -1;
345 : }
346 :
347 0 : recvalue = dbwrap_record_get_value(rec);
348 :
349 0 : if (recvalue.dptr == 0) { /* first entry */
350 0 : status = dbwrap_fetch(ctx->db, ctx->diff, key, &diff.oval);
351 0 : if (!NT_STATUS_IS_OK(status)) {
352 0 : diff.oval = tdb_null;
353 : }
354 : } else {
355 0 : diff = unpack_diff(recvalue);
356 0 : talloc_free(diff.nval.dptr);
357 : }
358 0 : diff.nval = tdb_data_talloc_copy(ctx->diff, value);
359 :
360 0 : DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
361 :
362 0 : status = dbwrap_record_store(rec, pack_diff(&diff), 0);
363 :
364 0 : talloc_free(mem);
365 :
366 0 : if (!NT_STATUS_IS_OK(status)) {
367 0 : DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
368 0 : return -1;
369 : }
370 0 : ctx->n_diff ++;
371 0 : return 0;
372 : }
373 :
374 0 : static int del_record(struct check_ctx* ctx, TDB_DATA key) {
375 0 : return add_record(ctx, key, tdb_null);
376 : }
377 :
378 : static TDB_DATA
379 0 : fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
380 : {
381 0 : TDB_DATA tmp;
382 0 : NTSTATUS status;
383 :
384 0 : status = dbwrap_fetch(ctx->diff, mem_ctx, key, &tmp);
385 :
386 0 : if (NT_STATUS_IS_OK(status)) {
387 0 : TDB_DATA_diff diff = unpack_diff(tmp);
388 0 : TDB_DATA ret = tdb_data_talloc_copy(mem_ctx, diff.nval);
389 0 : talloc_free(tmp.dptr);
390 0 : return ret;
391 : }
392 :
393 0 : status = dbwrap_fetch(ctx->db, mem_ctx, key, &tmp);
394 0 : if (!NT_STATUS_IS_OK(status)) {
395 0 : return tdb_null;
396 : }
397 :
398 0 : return tmp;
399 : }
400 :
401 0 : static void edit_record(struct record* r) {
402 0 : TALLOC_CTX* mem = talloc_new(r);
403 0 : cbuf* ost = cbuf_new(mem);
404 0 : const char* str;
405 0 : struct record* nr;
406 0 : TDB_DATA key;
407 0 : TDB_DATA val;
408 0 : cbuf_printf(ost, "%s %s\n",
409 : print_data(mem, r->key), print_data(mem, r->val));
410 0 : str = interact_edit(mem, cbuf_gets(ost, 0));
411 0 : key = parse_data(mem, &str);
412 0 : val = parse_data(mem, &str);
413 0 : nr = parse_record(talloc_parent(r), key, val);
414 0 : if (nr != NULL) {
415 0 : *r = *nr;
416 : }
417 0 : talloc_free(mem);
418 0 : }
419 :
420 0 : static bool check_version(struct check_ctx* ctx) {
421 0 : static const char* key = "IDMAP_VERSION";
422 0 : uint32_t version;
423 0 : NTSTATUS status;
424 0 : char action = 's';
425 0 : struct check_actions* act = &ctx->action;
426 :
427 0 : status = dbwrap_fetch_uint32_bystring(ctx->db, key, &version);
428 0 : if (!NT_STATUS_IS_OK(status)) {
429 0 : d_printf("No version number, assume 2\n");
430 0 : action = get_action(&act->no_version, NULL, NULL);
431 0 : } else if (version != 2) {
432 0 : d_printf("Wrong version number %d, should be 2\n", version);
433 0 : action = get_action(&act->wrong_version, NULL, NULL);
434 : }
435 0 : switch (action) {
436 0 : case 's':
437 0 : break;
438 0 : case 'f':
439 0 : SIVAL(&version, 0, 2);
440 0 : add_record(ctx, string_term_tdb_data(key),
441 : make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
442 0 : break;
443 0 : case 'a':
444 0 : return false;
445 0 : default:
446 0 : assert(false);
447 : }
448 0 : return true;
449 : }
450 :
451 0 : static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
452 0 : uint32_t hwm;
453 0 : char action = 's';
454 0 : NTSTATUS status;
455 0 : struct check_actions* act = &ctx->action;
456 :
457 0 : status = dbwrap_fetch_uint32_bystring(ctx->db, key, &hwm);
458 0 : if (!NT_STATUS_IS_OK(status)) {
459 0 : d_printf("No %s should be %d\n", key, target);
460 0 : action = get_action(&act->invalid_hwm, NULL, NULL);
461 0 : } else if (target < hwm) {
462 0 : d_printf("Invalid %s %d: should be %d\n", key, hwm, target);
463 0 : action = get_action(&act->invalid_hwm, NULL, NULL);
464 : }
465 0 : if (action == 'f') {
466 0 : SIVAL(&hwm, 0, target);
467 0 : add_record(ctx, string_term_tdb_data(key),
468 : make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
469 : }
470 0 : }
471 :
472 0 : int traverse_check(struct db_record *rec, void* data) {
473 0 : struct check_ctx* ctx = (struct check_ctx*)data;
474 0 : struct check_actions* act = &ctx->action;
475 0 : TALLOC_CTX* mem = talloc_new(ctx->diff);
476 0 : TDB_DATA key;
477 0 : TDB_DATA value;
478 0 : struct record *r;
479 0 : char action = 's';
480 :
481 0 : key = dbwrap_record_get_key(rec);
482 0 : value = dbwrap_record_get_value(rec);
483 :
484 0 : r = parse_record(mem, key, value);
485 :
486 0 : if (is_invalid(r)) {
487 0 : action = get_action(&act->invalid_record, r, NULL);
488 0 : ctx->n_invalid_record++;
489 0 : } else if (is_map(r)) {
490 0 : TDB_DATA back = fetch_record(ctx, mem, r->val);
491 0 : if (back.dptr == NULL) {
492 0 : action = get_action(&act->missing_reverse, r, NULL);
493 0 : ctx->n_missing_reverse++;
494 0 : } else if (!tdb_data_equal(r->key, back)) {
495 0 : action = get_action(&act->invalid_mapping, r, &back);
496 0 : ctx->n_invalid_mappping++;
497 : } else {
498 0 : if (r->key_type == DT_SID) {
499 0 : action = get_action(&act->valid_mapping, r, NULL);
500 0 : ctx->n_map++;
501 : } else {
502 0 : action = get_action(&act->valid_mapping, NULL,
503 : NULL);
504 : }
505 : }
506 0 : adjust_hwm(ctx, r);
507 : } else {
508 0 : action = get_action(&act->valid_other, r, NULL);
509 0 : ctx->n_other++;
510 : }
511 :
512 0 : while (action) {
513 0 : switch (action) {
514 0 : case 's': /* skip */
515 0 : break;
516 0 : case 'd': /* delete */
517 0 : del_record(ctx, key);
518 0 : break;
519 0 : case 'f': /* add reverse mapping */
520 0 : add_record(ctx, value, key);
521 0 : break;
522 0 : case 'e': /* edit */
523 0 : edit_record(r);
524 0 : action = 'o';
525 0 : if (is_invalid(r)) {
526 0 : action = get_action(&act->invalid_edit, r,NULL);
527 0 : continue;
528 : }
529 0 : if (!tdb_data_equal(key, r->key)) {
530 0 : TDB_DATA oval = fetch_record(ctx, mem, r->key);
531 0 : if (!tdb_data_is_empty(oval) &&
532 0 : !tdb_data_equal(oval, r->val))
533 : {
534 0 : action = get_action(&act->record_exists,
535 : r, &oval);
536 0 : if (action != 'o') {
537 0 : continue;
538 : }
539 : }
540 : }
541 0 : if (is_map(r)) {
542 0 : TDB_DATA okey = fetch_record(ctx, mem, r->val);
543 0 : if (!tdb_data_is_empty(okey) &&
544 0 : !tdb_data_equal(okey, r->key))
545 : {
546 0 : action = get_action(&act->record_exists,
547 : reverse_record(r),
548 : &okey);
549 : }
550 : }
551 0 : continue;
552 0 : case 'o': /* overwrite */
553 0 : adjust_hwm(ctx, r);
554 0 : if (!tdb_data_equal(key, r->key)) {
555 0 : del_record(ctx, key);
556 : }
557 0 : add_record(ctx, r->key, r->val);
558 0 : if (is_map(r)) {
559 0 : add_record(ctx, r->val, r->key);
560 : }
561 : }
562 0 : action = '\0';
563 0 : };
564 :
565 0 : talloc_free(mem);
566 :
567 0 : return 0;
568 : }
569 :
570 : /******************************************************************************/
571 :
572 0 : void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
573 0 : enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
574 0 : if (type == DT_UID) {
575 0 : ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
576 0 : } else if (type == DT_GID) {
577 0 : ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
578 : }
579 0 : }
580 :
581 0 : static bool is_cstr(TDB_DATA str) {
582 0 : return !tdb_data_is_empty(str) && str.dptr[str.dsize-1] == '\0';
583 : }
584 :
585 0 : static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
586 0 : struct dom_sid tmp;
587 0 : const char* s = (const char*)str.dptr;
588 0 : if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
589 0 : *sid = tmp;
590 0 : *type = DT_SID;
591 0 : return true;
592 : }
593 0 : return false;
594 : }
595 :
596 0 : static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
597 0 : char c, t;
598 0 : unsigned long tmp;
599 0 : if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
600 0 : if (c == 'U') {
601 0 : *id = tmp;
602 0 : *type = DT_UID;
603 0 : return true;
604 0 : } else if (c == 'G') {
605 0 : *id = tmp;
606 0 : *type = DT_GID;
607 0 : return true;
608 : }
609 : }
610 0 : return false;
611 : }
612 :
613 :
614 : struct record*
615 0 : parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
616 : {
617 0 : struct record* ret = talloc_zero(mem_ctx, struct record);
618 0 : if (ret == NULL) {
619 0 : DEBUG(0, ("Out of memory.\n"));
620 0 : return NULL;
621 : }
622 0 : ret->key = tdb_data_talloc_copy(ret, key);
623 0 : ret->val = tdb_data_talloc_copy(ret, val);
624 0 : if ((ret->key.dptr == NULL && key.dptr != NULL) ||
625 0 : (ret->val.dptr == NULL && val.dptr != NULL))
626 : {
627 0 : talloc_free(ret);
628 0 : DEBUG(0, ("Out of memory.\n"));
629 0 : return NULL;
630 : }
631 0 : assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
632 :
633 0 : if (!is_cstr(key)) {
634 0 : return ret;
635 : }
636 0 : if (parse_sid(key, &ret->key_type, &ret->sid)) {
637 0 : parse_xid(val, &ret->val_type, &ret->id);
638 0 : } else if (parse_xid(key, &ret->key_type, &ret->id)) {
639 0 : if (is_cstr(val)) {
640 0 : parse_sid(val, &ret->val_type, &ret->sid);
641 : }
642 0 : } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) {
643 0 : ret->key_type = DT_HWM;
644 0 : if (val.dsize == 4) {
645 0 : ret->id = IVAL(val.dptr,0);
646 0 : ret->val_type = DT_UID;
647 : }
648 0 : } else if (strcmp((const char*)key.dptr, "GROUP HWM") == 0) {
649 0 : ret->key_type = DT_HWM;
650 0 : if (val.dsize == 4) {
651 0 : ret->id = IVAL(val.dptr,0);
652 0 : ret->val_type = DT_GID;
653 : }
654 0 : } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) {
655 0 : ret->key_type = DT_VER;
656 0 : if (val.dsize == 4) {
657 0 : ret->id = IVAL(val.dptr,0);
658 0 : ret->val_type = DT_VER;
659 : }
660 0 : } else if (strcmp((const char*)key.dptr, "__db_sequence_number__") == 0) {
661 0 : ret->key_type = DT_SEQ;
662 0 : if (val.dsize == 8) {
663 0 : ret->id = *(uint64_t*)val.dptr;
664 0 : ret->val_type = DT_SEQ;
665 : }
666 : }
667 :
668 0 : return ret;
669 : }
670 :
671 0 : struct record* reverse_record(struct record* in)
672 : {
673 0 : return parse_record(talloc_parent(in), in->val, in->key);
674 : }
675 :
676 :
677 : /******************************************************************************/
678 :
679 :
680 0 : char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
681 : {
682 0 : if (!tdb_data_is_empty(d)) {
683 0 : char* ret = NULL;
684 0 : cbuf* ost = cbuf_new(mem_ctx);
685 0 : int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
686 0 : if (len != -1) {
687 0 : cbuf_swapptr(ost, &ret, 0);
688 0 : talloc_steal(mem_ctx, ret);
689 : }
690 0 : talloc_free(ost);
691 0 : return ret;
692 : }
693 0 : return talloc_strdup(mem_ctx, "<NULL>");
694 : }
695 :
696 :
697 0 : TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
698 0 : cbuf* ost = cbuf_new(mem_ctx);
699 0 : TDB_DATA ret = tdb_null;
700 0 : srprs_skipws(ptr);
701 0 : if (srprs_quoted(ptr, ost)) {
702 0 : ret.dsize = cbuf_getpos(ost);
703 0 : ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0));
704 : }
705 0 : talloc_free(ost);
706 0 : return ret;
707 : }
708 :
709 0 : static int traverse_print_diff(struct db_record *rec, void* data) {
710 0 : struct check_ctx* ctx = (struct check_ctx*)data;
711 0 : TDB_DATA key;
712 0 : TDB_DATA value;
713 0 : TDB_DATA_diff diff;
714 0 : TALLOC_CTX* mem = talloc_new(ctx->diff);
715 :
716 0 : key = dbwrap_record_get_key(rec);
717 0 : value = dbwrap_record_get_value(rec);
718 0 : diff = unpack_diff(value);
719 :
720 0 : DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
721 :
722 0 : talloc_free(mem);
723 0 : return 0;
724 : }
725 :
726 :
727 0 : static int traverse_commit(struct db_record *diff_rec, void* data) {
728 0 : struct check_ctx* ctx = (struct check_ctx*)data;
729 0 : TDB_DATA key;
730 0 : TDB_DATA diff_value;
731 0 : TDB_DATA_diff diff;
732 0 : TDB_DATA value;
733 0 : TALLOC_CTX* mem = talloc_new(ctx->diff);
734 0 : int ret = -1;
735 0 : NTSTATUS status;
736 0 : struct check_actions* act = &ctx->action;
737 0 : struct db_record* rec;
738 :
739 0 : key = dbwrap_record_get_key(diff_rec);
740 0 : diff_value = dbwrap_record_get_value(diff_rec);
741 0 : diff = unpack_diff(diff_value);
742 :
743 0 : rec = dbwrap_fetch_locked(ctx->db, mem, key);
744 0 : if (rec == NULL) {
745 0 : goto done;
746 : }
747 :
748 0 : value = dbwrap_record_get_value(rec);
749 :
750 0 : if (!tdb_data_equal(value, diff.oval)) {
751 0 : char action;
752 :
753 0 : d_printf("Warning: record has changed: %s\n"
754 : "expected: %s got %s\n", print_data(mem, key),
755 : print_data(mem, diff.oval),
756 : print_data(mem, value));
757 :
758 0 : action = get_action(&act->invalid_diff, NULL, NULL);
759 0 : if (action == 's') {
760 0 : ret = 0;
761 0 : goto done;
762 0 : } else if (action == 'a') {
763 0 : goto done;
764 : }
765 : }
766 :
767 0 : DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
768 :
769 0 : if (tdb_data_is_empty(diff.nval)) {
770 0 : status = dbwrap_record_delete(rec);
771 : } else {
772 0 : status = dbwrap_record_store(rec, diff.nval, 0);
773 : }
774 :
775 0 : if (!NT_STATUS_IS_OK(status)) {
776 0 : DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
777 0 : if (!ctx->opts.force) {
778 0 : goto done;
779 : }
780 : }
781 0 : ret = 0;
782 0 : done:
783 0 : talloc_free(mem);
784 0 : return ret;
785 : }
786 :
787 : static struct check_ctx*
788 0 : check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
789 : {
790 0 : struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
791 0 : if (ctx == NULL) {
792 0 : DEBUG(0, (_("No memory\n")));
793 0 : return NULL;
794 : }
795 :
796 0 : ctx->diff = db_open_rbt(ctx);
797 0 : if (ctx->diff == NULL) {
798 0 : talloc_free(ctx);
799 0 : DEBUG(0, (_("No memory\n")));
800 0 : return NULL;
801 : }
802 :
803 0 : ctx->action = check_actions_init(o);
804 0 : ctx->opts = *o;
805 0 : return ctx;
806 : }
807 :
808 0 : static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
809 : {
810 0 : if (name == NULL) {
811 0 : d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
812 0 : return false;
813 : }
814 :
815 0 : if (ctx->db != NULL) {
816 0 : if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
817 0 : return true;
818 : } else {
819 0 : TALLOC_FREE(ctx->db);
820 : }
821 : }
822 :
823 0 : ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0,
824 : DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
825 0 : if (ctx->db == NULL) {
826 0 : d_fprintf(stderr,
827 0 : _("Could not open idmap db (%s) for writing: %s\n"),
828 0 : name, strerror(errno));
829 0 : return false;
830 : }
831 :
832 0 : if (ctx->name != name) {
833 0 : TALLOC_FREE(ctx->name);
834 0 : ctx->name = talloc_strdup(ctx, name);
835 : }
836 :
837 0 : ctx->oflags = oflags;
838 0 : return true;
839 : }
840 :
841 0 : static bool check_do_checks(struct check_ctx* ctx)
842 : {
843 0 : NTSTATUS status;
844 :
845 0 : if (!check_version(ctx)) {
846 0 : return false;
847 : }
848 :
849 0 : status = dbwrap_traverse(ctx->db, traverse_check, ctx, NULL);
850 :
851 0 : if (!NT_STATUS_IS_OK(status)) {
852 0 : DEBUG(0, ("failed to traverse %s\n", ctx->name));
853 0 : return false;
854 : }
855 :
856 0 : check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
857 0 : check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
858 :
859 0 : return true;
860 : }
861 :
862 0 : static void check_summary(const struct check_ctx* ctx)
863 : {
864 0 : d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm);
865 0 : d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other);
866 0 : d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
867 0 : ctx->n_invalid_record, ctx->n_missing_reverse,
868 0 : ctx->n_invalid_mappping);
869 0 : d_printf("%u changes:\n", ctx->n_diff);
870 0 : }
871 :
872 0 : static bool check_transaction_start(struct check_ctx* ctx) {
873 0 : return (dbwrap_transaction_start(ctx->db) == 0);
874 : }
875 :
876 0 : static bool check_transaction_commit(struct check_ctx* ctx) {
877 0 : return (dbwrap_transaction_commit(ctx->db) == 0);
878 : }
879 :
880 0 : static bool check_transaction_cancel(struct check_ctx* ctx) {
881 0 : return (dbwrap_transaction_cancel(ctx->db) == 0);
882 : }
883 :
884 :
885 0 : static void check_diff_list(struct check_ctx* ctx) {
886 0 : NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx, NULL);
887 :
888 0 : if (!NT_STATUS_IS_OK(status)) {
889 0 : DEBUG(0, ("failed to traverse diff\n"));
890 : }
891 :
892 0 : }
893 :
894 0 : static bool check_commit(struct check_ctx* ctx)
895 : {
896 0 : struct check_actions* act = &ctx->action;
897 0 : char action;
898 0 : NTSTATUS status = NT_STATUS_OK;
899 :
900 0 : check_summary(ctx);
901 :
902 0 : if (ctx->n_diff == 0) {
903 0 : return true;
904 : }
905 :
906 0 : while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
907 0 : check_diff_list(ctx);
908 : }
909 0 : if (action == 's') {
910 0 : return true;
911 : }
912 0 : assert(action == 'c');
913 :
914 0 : if (!check_open_db(ctx, ctx->name, O_RDWR)) {
915 0 : return false;
916 : }
917 :
918 0 : if (!check_transaction_start(ctx)) {
919 0 : return false;
920 : }
921 :
922 0 : status = dbwrap_traverse(ctx->diff, traverse_commit, ctx, NULL);
923 :
924 0 : if (!NT_STATUS_IS_OK(status)) {
925 0 : check_transaction_cancel(ctx);
926 0 : return false;
927 : }
928 0 : if (ctx->opts.test) { /*get_action? */
929 0 : return check_transaction_cancel(ctx);
930 : } else {
931 0 : return check_transaction_commit(ctx);
932 : }
933 : }
934 :
935 0 : int net_idmap_check_db(const char* db, const struct check_options* o)
936 : {
937 0 : int ret = -1;
938 0 : TALLOC_CTX* mem_ctx = talloc_stackframe();
939 0 : struct check_ctx* ctx = check_init(mem_ctx, o);
940 :
941 0 : if (!o->automatic && !isatty(STDIN_FILENO)) {
942 0 : DEBUG(0, ("Interactive use needs tty, use --auto\n"));
943 0 : goto done;
944 : }
945 0 : if (o->lock) {
946 0 : if (check_open_db(ctx, db, O_RDWR)
947 0 : && check_transaction_start(ctx))
948 : {
949 0 : if ( check_do_checks(ctx)
950 0 : && check_commit(ctx)
951 0 : && check_transaction_commit(ctx))
952 : {
953 0 : ret = 0;
954 : } else {
955 0 : check_transaction_cancel(ctx);
956 : }
957 : }
958 : } else {
959 0 : if (check_open_db(ctx, db, O_RDONLY)
960 0 : && check_do_checks(ctx)
961 0 : && check_commit(ctx))
962 : {
963 0 : ret = 0;
964 : }
965 : }
966 0 : done:
967 0 : talloc_free(mem_ctx);
968 0 : return ret;
969 : }
970 :
971 :
972 : /*Local Variables:*/
973 : /*mode: c*/
974 : /*End:*/
|