| 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 <[email protected]>
|
|---|
| 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:*/
|
|---|