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 | static bool is_invalid(const struct record* r) {
|
---|
65 | return (r->key_type == DT_INV) || (r->val_type == DT_INV);
|
---|
66 | }
|
---|
67 |
|
---|
68 | static bool is_map(const struct record* r) {
|
---|
69 | return (r->key_type == DT_SID)
|
---|
70 | || (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 | static void invalid_mapping_fmt(struct check_action *a,
|
---|
103 | struct record *r,
|
---|
104 | TDB_DATA *v)
|
---|
105 | {
|
---|
106 | 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 | (v ? print_data(r, *v) : ""));
|
---|
111 | }
|
---|
112 |
|
---|
113 | static void record_exists_fmt(struct check_action *a,
|
---|
114 | struct record *r,
|
---|
115 | TDB_DATA *v)
|
---|
116 | {
|
---|
117 | 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 | (v ? print_data(r, *v) : ""));
|
---|
122 | }
|
---|
123 |
|
---|
124 | static void valid_mapping_fmt(struct check_action *a,
|
---|
125 | struct record *r,
|
---|
126 | TDB_DATA *v)
|
---|
127 | {
|
---|
128 | 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 | }
|
---|
133 |
|
---|
134 | static struct check_actions
|
---|
135 | check_actions_init(const struct check_options* opts) {
|
---|
136 | 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 | .verbose = opts->verbose,
|
---|
205 | },
|
---|
206 | .valid_other = (check_action) {
|
---|
207 | .name = "Other",
|
---|
208 | .auto_action = 's',
|
---|
209 | .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 | if (!opts->repair) {
|
---|
220 | ret.invalid_record.auto_action = 's';
|
---|
221 | ret.missing_reverse.auto_action = 's';
|
---|
222 | ret.invalid_mapping.auto_action = 's';
|
---|
223 | ret.no_version.auto_action = 's';
|
---|
224 | ret.wrong_version.auto_action = 's';
|
---|
225 | ret.invalid_hwm.auto_action = 's';
|
---|
226 | ret.commit.auto_action = 's';
|
---|
227 | }
|
---|
228 |
|
---|
229 | if (opts->automatic) {
|
---|
230 | ret.invalid_record.auto_action = 'd'; /* delete */
|
---|
231 | ret.missing_reverse.auto_action = 'f'; /* fix */
|
---|
232 | ret.invalid_mapping.auto_action = 'd'; /* delete */
|
---|
233 | ret.no_version.auto_action = 'f'; /* fix */
|
---|
234 | ret.wrong_version.auto_action = 'a'; /* abort */
|
---|
235 | ret.invalid_hwm.auto_action = 'f'; /* fix */
|
---|
236 | ret.commit.auto_action = 'c'; /* commit */
|
---|
237 | ret.invalid_diff.auto_action = 'a'; /* abort */
|
---|
238 | if (opts->force) {
|
---|
239 | ret.wrong_version.auto_action = 'f'; /* fix */
|
---|
240 | ret.invalid_diff.auto_action = 'c'; /* commit */
|
---|
241 | }
|
---|
242 | }
|
---|
243 | if (opts->test) {
|
---|
244 | ret.invalid_diff.auto_action = 'c'; /* commit */
|
---|
245 | /* ret.commit.auto_action = 'c';*/ /* commit */
|
---|
246 | }
|
---|
247 |
|
---|
248 | return ret;
|
---|
249 | }
|
---|
250 |
|
---|
251 | static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
|
---|
252 | char ret;
|
---|
253 | if (a->verbose && (r != NULL)) {
|
---|
254 | if (!a->fmt) {
|
---|
255 | d_printf("%s: %s ", a->name, print_data(r, r->key));
|
---|
256 | if (is_map(r)) {
|
---|
257 | d_printf("-> %s\n", print_data(r, r->val));
|
---|
258 | } else if (r->key_type == DT_HWM ||
|
---|
259 | r->key_type == DT_VER ||
|
---|
260 | r->key_type == DT_SEQ)
|
---|
261 | {
|
---|
262 | d_printf(": %ld\n", r->id);
|
---|
263 | } else {
|
---|
264 | d_printf("\n");
|
---|
265 | }
|
---|
266 | } else {
|
---|
267 | a->fmt(a, r, v);
|
---|
268 | }
|
---|
269 | }
|
---|
270 |
|
---|
271 | if (a->auto_action != '\0') {
|
---|
272 | return a->auto_action;
|
---|
273 | }
|
---|
274 |
|
---|
275 | ret = interact_prompt(a->prompt, a->answers, a->default_action);
|
---|
276 |
|
---|
277 | if (isupper(ret)) {
|
---|
278 | ret = tolower(ret);
|
---|
279 | a->auto_action = ret;
|
---|
280 | }
|
---|
281 | a->default_action = ret;
|
---|
282 | return ret;
|
---|
283 | }
|
---|
284 |
|
---|
285 | /* *************************************************************************/
|
---|
286 |
|
---|
287 | typedef struct {
|
---|
288 | TDB_DATA oval, nval;
|
---|
289 | } TDB_DATA_diff;
|
---|
290 |
|
---|
291 | static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
|
---|
292 | return (TDB_DATA) {
|
---|
293 | .dptr = (uint8_t *)diff,
|
---|
294 | .dsize = sizeof(TDB_DATA_diff),
|
---|
295 | };
|
---|
296 | }
|
---|
297 |
|
---|
298 | static TDB_DATA_diff unpack_diff(TDB_DATA data) {
|
---|
299 | assert(data.dsize == sizeof(TDB_DATA_diff));
|
---|
300 | 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 | static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
|
---|
336 | {
|
---|
337 | NTSTATUS status;
|
---|
338 | TDB_DATA_diff diff;
|
---|
339 | TALLOC_CTX* mem = talloc_new(ctx->diff);
|
---|
340 | TDB_DATA recvalue;
|
---|
341 | struct db_record *rec = dbwrap_fetch_locked(ctx->diff, mem, key);
|
---|
342 |
|
---|
343 | if (rec == NULL) {
|
---|
344 | return -1;
|
---|
345 | }
|
---|
346 |
|
---|
347 | recvalue = dbwrap_record_get_value(rec);
|
---|
348 |
|
---|
349 | if (recvalue.dptr == 0) { /* first entry */
|
---|
350 | status = dbwrap_fetch(ctx->db, ctx->diff, key, &diff.oval);
|
---|
351 | if (!NT_STATUS_IS_OK(status)) {
|
---|
352 | diff.oval = tdb_null;
|
---|
353 | }
|
---|
354 | } else {
|
---|
355 | diff = unpack_diff(recvalue);
|
---|
356 | talloc_free(diff.nval.dptr);
|
---|
357 | }
|
---|
358 | diff.nval = tdb_data_talloc_copy(ctx->diff, value);
|
---|
359 |
|
---|
360 | DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
|
---|
361 |
|
---|
362 | status = dbwrap_record_store(rec, pack_diff(&diff), 0);
|
---|
363 |
|
---|
364 | talloc_free(mem);
|
---|
365 |
|
---|
366 | if (!NT_STATUS_IS_OK(status)) {
|
---|
367 | DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
|
---|
368 | return -1;
|
---|
369 | }
|
---|
370 | ctx->n_diff ++;
|
---|
371 | return 0;
|
---|
372 | }
|
---|
373 |
|
---|
374 | static int del_record(struct check_ctx* ctx, TDB_DATA key) {
|
---|
375 | return add_record(ctx, key, tdb_null);
|
---|
376 | }
|
---|
377 |
|
---|
378 | static TDB_DATA
|
---|
379 | fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
|
---|
380 | {
|
---|
381 | TDB_DATA tmp;
|
---|
382 | NTSTATUS status;
|
---|
383 |
|
---|
384 | status = dbwrap_fetch(ctx->diff, mem_ctx, key, &tmp);
|
---|
385 |
|
---|
386 | if (NT_STATUS_IS_OK(status)) {
|
---|
387 | TDB_DATA_diff diff = unpack_diff(tmp);
|
---|
388 | TDB_DATA ret = tdb_data_talloc_copy(mem_ctx, diff.nval);
|
---|
389 | talloc_free(tmp.dptr);
|
---|
390 | return ret;
|
---|
391 | }
|
---|
392 |
|
---|
393 | status = dbwrap_fetch(ctx->db, mem_ctx, key, &tmp);
|
---|
394 | if (!NT_STATUS_IS_OK(status)) {
|
---|
395 | return tdb_null;
|
---|
396 | }
|
---|
397 |
|
---|
398 | return tmp;
|
---|
399 | }
|
---|
400 |
|
---|
401 | static void edit_record(struct record* r) {
|
---|
402 | TALLOC_CTX* mem = talloc_new(r);
|
---|
403 | cbuf* ost = cbuf_new(mem);
|
---|
404 | const char* str;
|
---|
405 | struct record* nr;
|
---|
406 | TDB_DATA key;
|
---|
407 | TDB_DATA val;
|
---|
408 | cbuf_printf(ost, "%s %s\n",
|
---|
409 | print_data(mem, r->key), print_data(mem, r->val));
|
---|
410 | str = interact_edit(mem, cbuf_gets(ost, 0));
|
---|
411 | key = parse_data(mem, &str);
|
---|
412 | val = parse_data(mem, &str);
|
---|
413 | nr = parse_record(talloc_parent(r), key, val);
|
---|
414 | if (nr != NULL) {
|
---|
415 | *r = *nr;
|
---|
416 | }
|
---|
417 | talloc_free(mem);
|
---|
418 | }
|
---|
419 |
|
---|
420 | static bool check_version(struct check_ctx* ctx) {
|
---|
421 | static const char* key = "IDMAP_VERSION";
|
---|
422 | uint32_t version;
|
---|
423 | NTSTATUS status;
|
---|
424 | char action = 's';
|
---|
425 | struct check_actions* act = &ctx->action;
|
---|
426 |
|
---|
427 | status = dbwrap_fetch_uint32_bystring(ctx->db, key, &version);
|
---|
428 | if (!NT_STATUS_IS_OK(status)) {
|
---|
429 | d_printf("No version number, assume 2\n");
|
---|
430 | action = get_action(&act->no_version, NULL, NULL);
|
---|
431 | } else if (version != 2) {
|
---|
432 | d_printf("Wrong version number %d, should be 2\n", version);
|
---|
433 | action = get_action(&act->wrong_version, NULL, NULL);
|
---|
434 | }
|
---|
435 | switch (action) {
|
---|
436 | case 's':
|
---|
437 | break;
|
---|
438 | case 'f':
|
---|
439 | SIVAL(&version, 0, 2);
|
---|
440 | add_record(ctx, string_term_tdb_data(key),
|
---|
441 | make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
|
---|
442 | break;
|
---|
443 | case 'a':
|
---|
444 | return false;
|
---|
445 | default:
|
---|
446 | assert(false);
|
---|
447 | }
|
---|
448 | return true;
|
---|
449 | }
|
---|
450 |
|
---|
451 | static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
|
---|
452 | uint32_t hwm;
|
---|
453 | char action = 's';
|
---|
454 | NTSTATUS status;
|
---|
455 | struct check_actions* act = &ctx->action;
|
---|
456 |
|
---|
457 | status = dbwrap_fetch_uint32_bystring(ctx->db, key, &hwm);
|
---|
458 | if (!NT_STATUS_IS_OK(status)) {
|
---|
459 | d_printf("No %s should be %d\n", key, target);
|
---|
460 | action = get_action(&act->invalid_hwm, NULL, NULL);
|
---|
461 | } else if (target < hwm) {
|
---|
462 | d_printf("Invalid %s %d: should be %d\n", key, hwm, target);
|
---|
463 | action = get_action(&act->invalid_hwm, NULL, NULL);
|
---|
464 | }
|
---|
465 | if (action == 'f') {
|
---|
466 | SIVAL(&hwm, 0, target);
|
---|
467 | add_record(ctx, string_term_tdb_data(key),
|
---|
468 | make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
|
---|
469 | }
|
---|
470 | }
|
---|
471 |
|
---|
472 | int traverse_check(struct db_record *rec, void* data) {
|
---|
473 | struct check_ctx* ctx = (struct check_ctx*)data;
|
---|
474 | struct check_actions* act = &ctx->action;
|
---|
475 | TALLOC_CTX* mem = talloc_new(ctx->diff);
|
---|
476 | TDB_DATA key;
|
---|
477 | TDB_DATA value;
|
---|
478 | struct record *r;
|
---|
479 | char action = 's';
|
---|
480 |
|
---|
481 | key = dbwrap_record_get_key(rec);
|
---|
482 | value = dbwrap_record_get_value(rec);
|
---|
483 |
|
---|
484 | r = parse_record(mem, key, value);
|
---|
485 |
|
---|
486 | if (is_invalid(r)) {
|
---|
487 | action = get_action(&act->invalid_record, r, NULL);
|
---|
488 | ctx->n_invalid_record++;
|
---|
489 | } else if (is_map(r)) {
|
---|
490 | TDB_DATA back = fetch_record(ctx, mem, r->val);
|
---|
491 | if (back.dptr == NULL) {
|
---|
492 | action = get_action(&act->missing_reverse, r, NULL);
|
---|
493 | ctx->n_missing_reverse++;
|
---|
494 | } else if (!tdb_data_equal(r->key, back)) {
|
---|
495 | action = get_action(&act->invalid_mapping, r, &back);
|
---|
496 | ctx->n_invalid_mappping++;
|
---|
497 | } else {
|
---|
498 | if (r->key_type == DT_SID) {
|
---|
499 | action = get_action(&act->valid_mapping, r, NULL);
|
---|
500 | ctx->n_map++;
|
---|
501 | } else {
|
---|
502 | action = get_action(&act->valid_mapping, NULL,
|
---|
503 | NULL);
|
---|
504 | }
|
---|
505 | }
|
---|
506 | adjust_hwm(ctx, r);
|
---|
507 | } else {
|
---|
508 | action = get_action(&act->valid_other, r, NULL);
|
---|
509 | ctx->n_other++;
|
---|
510 | }
|
---|
511 |
|
---|
512 | while (action) {
|
---|
513 | switch (action) {
|
---|
514 | case 's': /* skip */
|
---|
515 | break;
|
---|
516 | case 'd': /* delete */
|
---|
517 | del_record(ctx, key);
|
---|
518 | break;
|
---|
519 | case 'f': /* add reverse mapping */
|
---|
520 | add_record(ctx, value, key);
|
---|
521 | break;
|
---|
522 | case 'e': /* edit */
|
---|
523 | edit_record(r);
|
---|
524 | action = 'o';
|
---|
525 | if (is_invalid(r)) {
|
---|
526 | action = get_action(&act->invalid_edit, r,NULL);
|
---|
527 | continue;
|
---|
528 | }
|
---|
529 | if (!tdb_data_equal(key, r->key)) {
|
---|
530 | TDB_DATA oval = fetch_record(ctx, mem, r->key);
|
---|
531 | if (!tdb_data_is_empty(oval) &&
|
---|
532 | !tdb_data_equal(oval, r->val))
|
---|
533 | {
|
---|
534 | action = get_action(&act->record_exists,
|
---|
535 | r, &oval);
|
---|
536 | if (action != 'o') {
|
---|
537 | continue;
|
---|
538 | }
|
---|
539 | }
|
---|
540 | }
|
---|
541 | if (is_map(r)) {
|
---|
542 | TDB_DATA okey = fetch_record(ctx, mem, r->val);
|
---|
543 | if (!tdb_data_is_empty(okey) &&
|
---|
544 | !tdb_data_equal(okey, r->key))
|
---|
545 | {
|
---|
546 | action = get_action(&act->record_exists,
|
---|
547 | reverse_record(r),
|
---|
548 | &okey);
|
---|
549 | }
|
---|
550 | }
|
---|
551 | continue;
|
---|
552 | case 'o': /* overwrite */
|
---|
553 | adjust_hwm(ctx, r);
|
---|
554 | if (!tdb_data_equal(key, r->key)) {
|
---|
555 | del_record(ctx, key);
|
---|
556 | }
|
---|
557 | add_record(ctx, r->key, r->val);
|
---|
558 | if (is_map(r)) {
|
---|
559 | add_record(ctx, r->val, r->key);
|
---|
560 | }
|
---|
561 | }
|
---|
562 | action = '\0';
|
---|
563 | };
|
---|
564 |
|
---|
565 | talloc_free(mem);
|
---|
566 |
|
---|
567 | return 0;
|
---|
568 | }
|
---|
569 |
|
---|
570 | /******************************************************************************/
|
---|
571 |
|
---|
572 | void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
|
---|
573 | enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
|
---|
574 | if (type == DT_UID) {
|
---|
575 | ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
|
---|
576 | } else if (type == DT_GID) {
|
---|
577 | ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
|
---|
578 | }
|
---|
579 | }
|
---|
580 |
|
---|
581 | static bool is_cstr(TDB_DATA str) {
|
---|
582 | return !tdb_data_is_empty(str) && str.dptr[str.dsize-1] == '\0';
|
---|
583 | }
|
---|
584 |
|
---|
585 | static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
|
---|
586 | struct dom_sid tmp;
|
---|
587 | const char* s = (const char*)str.dptr;
|
---|
588 | if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
|
---|
589 | *sid = tmp;
|
---|
590 | *type = DT_SID;
|
---|
591 | return true;
|
---|
592 | }
|
---|
593 | return false;
|
---|
594 | }
|
---|
595 |
|
---|
596 | static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
|
---|
597 | char c, t;
|
---|
598 | unsigned long tmp;
|
---|
599 | if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
|
---|
600 | if (c == 'U') {
|
---|
601 | *id = tmp;
|
---|
602 | *type = DT_UID;
|
---|
603 | return true;
|
---|
604 | } else if (c == 'G') {
|
---|
605 | *id = tmp;
|
---|
606 | *type = DT_GID;
|
---|
607 | return true;
|
---|
608 | }
|
---|
609 | }
|
---|
610 | return false;
|
---|
611 | }
|
---|
612 |
|
---|
613 |
|
---|
614 | struct record*
|
---|
615 | parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
|
---|
616 | {
|
---|
617 | struct record* ret = talloc_zero(mem_ctx, struct record);
|
---|
618 | if (ret == NULL) {
|
---|
619 | DEBUG(0, ("Out of memory.\n"));
|
---|
620 | return NULL;
|
---|
621 | }
|
---|
622 | ret->key = tdb_data_talloc_copy(ret, key);
|
---|
623 | ret->val = tdb_data_talloc_copy(ret, val);
|
---|
624 | if ((ret->key.dptr == NULL && key.dptr != NULL) ||
|
---|
625 | (ret->val.dptr == NULL && val.dptr != NULL))
|
---|
626 | {
|
---|
627 | talloc_free(ret);
|
---|
628 | DEBUG(0, ("Out of memory.\n"));
|
---|
629 | return NULL;
|
---|
630 | }
|
---|
631 | assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
|
---|
632 |
|
---|
633 | if (!is_cstr(key)) {
|
---|
634 | return ret;
|
---|
635 | }
|
---|
636 | if (parse_sid(key, &ret->key_type, &ret->sid)) {
|
---|
637 | parse_xid(val, &ret->val_type, &ret->id);
|
---|
638 | } else if (parse_xid(key, &ret->key_type, &ret->id)) {
|
---|
639 | if (is_cstr(val)) {
|
---|
640 | parse_sid(val, &ret->val_type, &ret->sid);
|
---|
641 | }
|
---|
642 | } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) {
|
---|
643 | ret->key_type = DT_HWM;
|
---|
644 | if (val.dsize == 4) {
|
---|
645 | ret->id = IVAL(val.dptr,0);
|
---|
646 | ret->val_type = DT_UID;
|
---|
647 | }
|
---|
648 | } else if (strcmp((const char*)key.dptr, "GROUP HWM") == 0) {
|
---|
649 | ret->key_type = DT_HWM;
|
---|
650 | if (val.dsize == 4) {
|
---|
651 | ret->id = IVAL(val.dptr,0);
|
---|
652 | ret->val_type = DT_GID;
|
---|
653 | }
|
---|
654 | } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) {
|
---|
655 | ret->key_type = DT_VER;
|
---|
656 | if (val.dsize == 4) {
|
---|
657 | ret->id = IVAL(val.dptr,0);
|
---|
658 | ret->val_type = DT_VER;
|
---|
659 | }
|
---|
660 | } else if (strcmp((const char*)key.dptr, "__db_sequence_number__") == 0) {
|
---|
661 | ret->key_type = DT_SEQ;
|
---|
662 | if (val.dsize == 8) {
|
---|
663 | ret->id = *(uint64_t*)val.dptr;
|
---|
664 | ret->val_type = DT_SEQ;
|
---|
665 | }
|
---|
666 | }
|
---|
667 |
|
---|
668 | return ret;
|
---|
669 | }
|
---|
670 |
|
---|
671 | struct record* reverse_record(struct record* in)
|
---|
672 | {
|
---|
673 | return parse_record(talloc_parent(in), in->val, in->key);
|
---|
674 | }
|
---|
675 |
|
---|
676 |
|
---|
677 | /******************************************************************************/
|
---|
678 |
|
---|
679 |
|
---|
680 | char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
|
---|
681 | {
|
---|
682 | if (!tdb_data_is_empty(d)) {
|
---|
683 | char* ret = NULL;
|
---|
684 | cbuf* ost = cbuf_new(mem_ctx);
|
---|
685 | int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
|
---|
686 | if (len != -1) {
|
---|
687 | cbuf_swapptr(ost, &ret, 0);
|
---|
688 | talloc_steal(mem_ctx, ret);
|
---|
689 | }
|
---|
690 | talloc_free(ost);
|
---|
691 | return ret;
|
---|
692 | }
|
---|
693 | return talloc_strdup(mem_ctx, "<NULL>");
|
---|
694 | }
|
---|
695 |
|
---|
696 |
|
---|
697 | TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
|
---|
698 | cbuf* ost = cbuf_new(mem_ctx);
|
---|
699 | TDB_DATA ret = tdb_null;
|
---|
700 | srprs_skipws(ptr);
|
---|
701 | if (srprs_quoted(ptr, ost)) {
|
---|
702 | ret.dsize = cbuf_getpos(ost);
|
---|
703 | ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0));
|
---|
704 | }
|
---|
705 | talloc_free(ost);
|
---|
706 | return ret;
|
---|
707 | }
|
---|
708 |
|
---|
709 | static int traverse_print_diff(struct db_record *rec, void* data) {
|
---|
710 | struct check_ctx* ctx = (struct check_ctx*)data;
|
---|
711 | TDB_DATA key;
|
---|
712 | TDB_DATA value;
|
---|
713 | TDB_DATA_diff diff;
|
---|
714 | TALLOC_CTX* mem = talloc_new(ctx->diff);
|
---|
715 |
|
---|
716 | key = dbwrap_record_get_key(rec);
|
---|
717 | value = dbwrap_record_get_value(rec);
|
---|
718 | diff = unpack_diff(value);
|
---|
719 |
|
---|
720 | DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
|
---|
721 |
|
---|
722 | talloc_free(mem);
|
---|
723 | return 0;
|
---|
724 | }
|
---|
725 |
|
---|
726 |
|
---|
727 | static int traverse_commit(struct db_record *diff_rec, void* data) {
|
---|
728 | struct check_ctx* ctx = (struct check_ctx*)data;
|
---|
729 | TDB_DATA key;
|
---|
730 | TDB_DATA diff_value;
|
---|
731 | TDB_DATA_diff diff;
|
---|
732 | TDB_DATA value;
|
---|
733 | TALLOC_CTX* mem = talloc_new(ctx->diff);
|
---|
734 | int ret = -1;
|
---|
735 | NTSTATUS status;
|
---|
736 | struct check_actions* act = &ctx->action;
|
---|
737 | struct db_record* rec;
|
---|
738 |
|
---|
739 | key = dbwrap_record_get_key(diff_rec);
|
---|
740 | diff_value = dbwrap_record_get_value(diff_rec);
|
---|
741 | diff = unpack_diff(diff_value);
|
---|
742 |
|
---|
743 | rec = dbwrap_fetch_locked(ctx->db, mem, key);
|
---|
744 | if (rec == NULL) {
|
---|
745 | goto done;
|
---|
746 | }
|
---|
747 |
|
---|
748 | value = dbwrap_record_get_value(rec);
|
---|
749 |
|
---|
750 | if (!tdb_data_equal(value, diff.oval)) {
|
---|
751 | char action;
|
---|
752 |
|
---|
753 | 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 | action = get_action(&act->invalid_diff, NULL, NULL);
|
---|
759 | if (action == 's') {
|
---|
760 | ret = 0;
|
---|
761 | goto done;
|
---|
762 | } else if (action == 'a') {
|
---|
763 | goto done;
|
---|
764 | }
|
---|
765 | }
|
---|
766 |
|
---|
767 | DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
|
---|
768 |
|
---|
769 | if (tdb_data_is_empty(diff.nval)) {
|
---|
770 | status = dbwrap_record_delete(rec);
|
---|
771 | } else {
|
---|
772 | status = dbwrap_record_store(rec, diff.nval, 0);
|
---|
773 | }
|
---|
774 |
|
---|
775 | if (!NT_STATUS_IS_OK(status)) {
|
---|
776 | DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
|
---|
777 | if (!ctx->opts.force) {
|
---|
778 | goto done;
|
---|
779 | }
|
---|
780 | }
|
---|
781 | ret = 0;
|
---|
782 | done:
|
---|
783 | talloc_free(mem);
|
---|
784 | return ret;
|
---|
785 | }
|
---|
786 |
|
---|
787 | static struct check_ctx*
|
---|
788 | check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
|
---|
789 | {
|
---|
790 | struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
|
---|
791 | if (ctx == NULL) {
|
---|
792 | DEBUG(0, (_("No memory\n")));
|
---|
793 | return NULL;
|
---|
794 | }
|
---|
795 |
|
---|
796 | ctx->diff = db_open_rbt(ctx);
|
---|
797 | if (ctx->diff == NULL) {
|
---|
798 | talloc_free(ctx);
|
---|
799 | DEBUG(0, (_("No memory\n")));
|
---|
800 | return NULL;
|
---|
801 | }
|
---|
802 |
|
---|
803 | ctx->action = check_actions_init(o);
|
---|
804 | ctx->opts = *o;
|
---|
805 | return ctx;
|
---|
806 | }
|
---|
807 |
|
---|
808 | static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
|
---|
809 | {
|
---|
810 | if (name == NULL) {
|
---|
811 | d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
|
---|
812 | return false;
|
---|
813 | }
|
---|
814 |
|
---|
815 | if (ctx->db != NULL) {
|
---|
816 | if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
|
---|
817 | return true;
|
---|
818 | } else {
|
---|
819 | TALLOC_FREE(ctx->db);
|
---|
820 | }
|
---|
821 | }
|
---|
822 |
|
---|
823 | ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0,
|
---|
824 | DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
|
---|
825 | if (ctx->db == NULL) {
|
---|
826 | d_fprintf(stderr,
|
---|
827 | _("Could not open idmap db (%s) for writing: %s\n"),
|
---|
828 | name, strerror(errno));
|
---|
829 | return false;
|
---|
830 | }
|
---|
831 |
|
---|
832 | if (ctx->name != name) {
|
---|
833 | TALLOC_FREE(ctx->name);
|
---|
834 | ctx->name = talloc_strdup(ctx, name);
|
---|
835 | }
|
---|
836 |
|
---|
837 | ctx->oflags = oflags;
|
---|
838 | return true;
|
---|
839 | }
|
---|
840 |
|
---|
841 | static bool check_do_checks(struct check_ctx* ctx)
|
---|
842 | {
|
---|
843 | NTSTATUS status;
|
---|
844 |
|
---|
845 | if (!check_version(ctx)) {
|
---|
846 | return false;
|
---|
847 | }
|
---|
848 |
|
---|
849 | status = dbwrap_traverse(ctx->db, traverse_check, ctx, NULL);
|
---|
850 |
|
---|
851 | if (!NT_STATUS_IS_OK(status)) {
|
---|
852 | DEBUG(0, ("failed to traverse %s\n", ctx->name));
|
---|
853 | return false;
|
---|
854 | }
|
---|
855 |
|
---|
856 | check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
|
---|
857 | check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
|
---|
858 |
|
---|
859 | return true;
|
---|
860 | }
|
---|
861 |
|
---|
862 | static void check_summary(const struct check_ctx* ctx)
|
---|
863 | {
|
---|
864 | d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm);
|
---|
865 | d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other);
|
---|
866 | d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
|
---|
867 | ctx->n_invalid_record, ctx->n_missing_reverse,
|
---|
868 | ctx->n_invalid_mappping);
|
---|
869 | d_printf("%u changes:\n", ctx->n_diff);
|
---|
870 | }
|
---|
871 |
|
---|
872 | static bool check_transaction_start(struct check_ctx* ctx) {
|
---|
873 | return (dbwrap_transaction_start(ctx->db) == 0);
|
---|
874 | }
|
---|
875 |
|
---|
876 | static bool check_transaction_commit(struct check_ctx* ctx) {
|
---|
877 | return (dbwrap_transaction_commit(ctx->db) == 0);
|
---|
878 | }
|
---|
879 |
|
---|
880 | static bool check_transaction_cancel(struct check_ctx* ctx) {
|
---|
881 | return (dbwrap_transaction_cancel(ctx->db) == 0);
|
---|
882 | }
|
---|
883 |
|
---|
884 |
|
---|
885 | static void check_diff_list(struct check_ctx* ctx) {
|
---|
886 | NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx, NULL);
|
---|
887 |
|
---|
888 | if (!NT_STATUS_IS_OK(status)) {
|
---|
889 | DEBUG(0, ("failed to traverse diff\n"));
|
---|
890 | }
|
---|
891 |
|
---|
892 | }
|
---|
893 |
|
---|
894 | static bool check_commit(struct check_ctx* ctx)
|
---|
895 | {
|
---|
896 | struct check_actions* act = &ctx->action;
|
---|
897 | char action;
|
---|
898 | NTSTATUS status = NT_STATUS_OK;
|
---|
899 |
|
---|
900 | check_summary(ctx);
|
---|
901 |
|
---|
902 | if (ctx->n_diff == 0) {
|
---|
903 | return true;
|
---|
904 | }
|
---|
905 |
|
---|
906 | while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
|
---|
907 | check_diff_list(ctx);
|
---|
908 | }
|
---|
909 | if (action == 's') {
|
---|
910 | return true;
|
---|
911 | }
|
---|
912 | assert(action == 'c');
|
---|
913 |
|
---|
914 | if (!check_open_db(ctx, ctx->name, O_RDWR)) {
|
---|
915 | return false;
|
---|
916 | }
|
---|
917 |
|
---|
918 | if (!check_transaction_start(ctx)) {
|
---|
919 | return false;
|
---|
920 | }
|
---|
921 |
|
---|
922 | status = dbwrap_traverse(ctx->diff, traverse_commit, ctx, NULL);
|
---|
923 |
|
---|
924 | if (!NT_STATUS_IS_OK(status)) {
|
---|
925 | check_transaction_cancel(ctx);
|
---|
926 | return false;
|
---|
927 | }
|
---|
928 | if (ctx->opts.test) { /*get_action? */
|
---|
929 | return check_transaction_cancel(ctx);
|
---|
930 | } else {
|
---|
931 | return check_transaction_commit(ctx);
|
---|
932 | }
|
---|
933 | }
|
---|
934 |
|
---|
935 | int net_idmap_check_db(const char* db, const struct check_options* o)
|
---|
936 | {
|
---|
937 | int ret = -1;
|
---|
938 | TALLOC_CTX* mem_ctx = talloc_stackframe();
|
---|
939 | struct check_ctx* ctx = check_init(mem_ctx, o);
|
---|
940 |
|
---|
941 | if (!o->automatic && !isatty(STDIN_FILENO)) {
|
---|
942 | DEBUG(0, ("Interactive use needs tty, use --auto\n"));
|
---|
943 | goto done;
|
---|
944 | }
|
---|
945 | if (o->lock) {
|
---|
946 | if (check_open_db(ctx, db, O_RDWR)
|
---|
947 | && check_transaction_start(ctx))
|
---|
948 | {
|
---|
949 | if ( check_do_checks(ctx)
|
---|
950 | && check_commit(ctx)
|
---|
951 | && check_transaction_commit(ctx))
|
---|
952 | {
|
---|
953 | ret = 0;
|
---|
954 | } else {
|
---|
955 | check_transaction_cancel(ctx);
|
---|
956 | }
|
---|
957 | }
|
---|
958 | } else {
|
---|
959 | if (check_open_db(ctx, db, O_RDONLY)
|
---|
960 | && check_do_checks(ctx)
|
---|
961 | && check_commit(ctx))
|
---|
962 | {
|
---|
963 | ret = 0;
|
---|
964 | }
|
---|
965 | }
|
---|
966 | done:
|
---|
967 | talloc_free(mem_ctx);
|
---|
968 | return ret;
|
---|
969 | }
|
---|
970 |
|
---|
971 |
|
---|
972 | /*Local Variables:*/
|
---|
973 | /*mode: c*/
|
---|
974 | /*End:*/
|
---|