LCOV - code coverage report
Current view: top level - source3/utils - net_idmap_check.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 0 451 0.0 %
Date: 2021-09-23 10:06:22 Functions: 0 40 0.0 %

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

Generated by: LCOV version 1.13