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