autorid: when storing a new range, always check it does not exist.
[sfrench/samba-autobuild/.git] / source3 / winbindd / idmap_autorid_tdb.c
1 /*
2  *  idmap_autorid_tdb: This file contains common code used by
3  *  idmap_autorid and net idmap autorid utilities. The common
4  *  code provides functions for performing various operations
5  *  on autorid.tdb
6  *
7  *  Copyright (C) Christian Ambach, 2010-2012
8  *  Copyright (C) Atul Kulkarni, 2013
9  *  Copyright (C) Michael Adam, 2012-2013
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 3 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
23  *
24  */
25
26 #include "idmap_autorid_tdb.h"
27 #include "../libcli/security/dom_sid.h"
28
29 /**
30  * Build the database keystring for getting a range
31  * belonging to a domain sid and a range index.
32  */
33 static void idmap_autorid_build_keystr(const char *domsid,
34                                        uint32_t domain_range_index,
35                                        fstring keystr)
36 {
37         if (domain_range_index > 0) {
38                 fstr_sprintf(keystr, "%s#%"PRIu32,
39                              domsid, domain_range_index);
40         } else {
41                 fstrcpy(keystr, domsid);
42         }
43 }
44
45 static char *idmap_autorid_build_keystr_talloc(TALLOC_CTX *mem_ctx,
46                                               const char *domsid,
47                                               uint32_t domain_range_index)
48 {
49         char *keystr;
50
51         if (domain_range_index > 0) {
52                 keystr = talloc_asprintf(mem_ctx, "%s#%"PRIu32, domsid,
53                                          domain_range_index);
54         } else {
55                 keystr = talloc_strdup(mem_ctx, domsid);
56         }
57
58         return keystr;
59 }
60
61
62 static bool idmap_autorid_validate_sid(const char *sid)
63 {
64         struct dom_sid ignore;
65         if (sid == NULL) {
66                 return false;
67         }
68
69         if (strcmp(sid, ALLOC_RANGE) == 0) {
70                 return true;
71         }
72
73         return dom_sid_parse(sid, &ignore);
74 }
75
76 struct idmap_autorid_addrange_ctx {
77         struct autorid_range_config *range;
78         bool acquire;
79 };
80
81 static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
82                                               void *private_data)
83 {
84         struct idmap_autorid_addrange_ctx *ctx;
85         uint32_t requested_rangenum, stored_rangenum;
86         struct autorid_range_config *range;
87         bool acquire;
88         NTSTATUS ret;
89         uint32_t hwm;
90         char *numstr;
91         struct autorid_global_config *globalcfg;
92         fstring keystr;
93         uint32_t increment;
94         TALLOC_CTX *mem_ctx = NULL;
95
96         ctx = (struct idmap_autorid_addrange_ctx *)private_data;
97         range = ctx->range;
98         acquire = ctx->acquire;
99         requested_rangenum = range->rangenum;
100
101         if (db == NULL) {
102                 DEBUG(3, ("Invalid database argument: NULL"));
103                 return NT_STATUS_INVALID_PARAMETER;
104         }
105
106         if (range == NULL) {
107                 DEBUG(3, ("Invalid range argument: NULL"));
108                 return NT_STATUS_INVALID_PARAMETER;
109         }
110
111         DEBUG(10, ("Adding new range for domain %s "
112                    "(domain_range_index=%"PRIu32")\n",
113                    range->domsid, range->domain_range_index));
114
115         if (!idmap_autorid_validate_sid(range->domsid)) {
116                 DEBUG(3, ("Invalid SID: %s\n", range->domsid));
117                 return NT_STATUS_INVALID_PARAMETER;
118         }
119
120         idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
121                                    keystr);
122
123         ret = dbwrap_fetch_uint32_bystring(db, keystr, &stored_rangenum);
124
125         if (NT_STATUS_IS_OK(ret)) {
126                 /* entry is already present*/
127                 if (acquire) {
128                         DEBUG(10, ("domain range already allocated - "
129                                    "Not adding!\n"));
130                         return NT_STATUS_OK;
131                 }
132
133                 if (stored_rangenum != requested_rangenum) {
134                         DEBUG(1, ("Error: requested rangenumber (%u) differs "
135                                   "from stored one (%u).\n",
136                                   requested_rangenum, stored_rangenum));
137                         return NT_STATUS_UNSUCCESSFUL;
138                 }
139
140                 DEBUG(10, ("Note: stored range agrees with requested "
141                            "one - ok\n"));
142                 return NT_STATUS_OK;
143         }
144
145         /* fetch the current HWM */
146         ret = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
147         if (!NT_STATUS_IS_OK(ret)) {
148                 DEBUG(1, ("Fatal error while fetching current "
149                           "HWM value: %s\n", nt_errstr(ret)));
150                 return NT_STATUS_INTERNAL_ERROR;
151         }
152
153         mem_ctx = talloc_stackframe();
154
155         ret = idmap_autorid_loadconfig(db, mem_ctx, &globalcfg);
156         if (!NT_STATUS_IS_OK(ret)) {
157                 DEBUG(1, ("Fatal error while fetching configuration: %s\n",
158                           nt_errstr(ret)));
159                 goto error;
160         }
161
162         if (acquire) {
163                 /*
164                  * automatically acquire the next range
165                  */
166                 requested_rangenum = hwm;
167         }
168
169         if (requested_rangenum >= globalcfg->maxranges) {
170                 DEBUG(1, ("Not enough ranges available: New range %u must be "
171                           "smaller than configured maximum number of ranges "
172                           "(%u).\n",
173                           requested_rangenum, globalcfg->maxranges));
174                 ret = NT_STATUS_NO_MEMORY;
175                 goto error;
176         }
177
178         /*
179          * Check that it is not yet taken.
180          * If the range is requested and < HWM, we need
181          * to check anyways, and otherwise, we also better
182          * check in order to prevent further corruption
183          * in case the db has been externally modified.
184          */
185
186         numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
187         if (!numstr) {
188                 ret = NT_STATUS_NO_MEMORY;
189                 goto error;
190         }
191
192         if (dbwrap_exists(db, string_term_tdb_data(numstr))) {
193                 DEBUG(1, ("Requested range '%s' is already in use.\n", numstr));
194
195                 if (requested_rangenum < hwm) {
196                         ret = NT_STATUS_INVALID_PARAMETER;
197                 } else {
198                         ret = NT_STATUS_INTERNAL_DB_CORRUPTION;
199                 }
200
201                 goto error;
202         }
203
204         if (requested_rangenum >= hwm) {
205                 /*
206                  * requested or automatic range >= HWM:
207                  * increment the HWM.
208                  */
209
210                 /* HWM always contains current max range + 1 */
211                 increment = requested_rangenum + 1 - hwm;
212
213                 /* increase the HWM */
214                 ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &hwm,
215                                                            increment);
216                 if (!NT_STATUS_IS_OK(ret)) {
217                         DEBUG(1, ("Fatal error while incrementing the HWM "
218                                   "value in the database: %s\n",
219                                   nt_errstr(ret)));
220                         goto error;
221                 }
222         }
223
224         /*
225          * store away the new mapping in both directions
226          */
227
228         ret = dbwrap_store_uint32_bystring(db, keystr, requested_rangenum);
229         if (!NT_STATUS_IS_OK(ret)) {
230                 DEBUG(1, ("Fatal error while storing new "
231                           "domain->range assignment: %s\n", nt_errstr(ret)));
232                 goto error;
233         }
234
235         numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
236         if (!numstr) {
237                 ret = NT_STATUS_NO_MEMORY;
238                 goto error;
239         }
240
241         ret = dbwrap_store_bystring(db, numstr,
242                         string_term_tdb_data(keystr), TDB_INSERT);
243
244         if (!NT_STATUS_IS_OK(ret)) {
245                 DEBUG(1, ("Fatal error while storing new "
246                           "domain->range assignment: %s\n", nt_errstr(ret)));
247                 goto error;
248         }
249         DEBUG(5, ("Acquired new range #%d for domain %s "
250                   "(domain_range_index=%"PRIu32")\n", requested_rangenum, keystr,
251                   range->domain_range_index));
252
253         range->rangenum = requested_rangenum;
254
255         range->low_id = globalcfg->minvalue
256                       + range->rangenum * globalcfg->rangesize;
257
258         ret = NT_STATUS_OK;
259
260 error:
261         talloc_free(mem_ctx);
262         return ret;
263 }
264
265 static NTSTATUS idmap_autorid_addrange(struct db_context *db,
266                                        struct autorid_range_config *range,
267                                        bool acquire)
268 {
269         NTSTATUS status;
270         struct idmap_autorid_addrange_ctx ctx;
271
272         ctx.acquire = acquire;
273         ctx.range = range;
274
275         status = dbwrap_trans_do(db, idmap_autorid_addrange_action, &ctx);
276         return status;
277 }
278
279 NTSTATUS idmap_autorid_setrange(struct db_context *db,
280                                 const char *domsid,
281                                 uint32_t domain_range_index,
282                                 uint32_t rangenum)
283 {
284         NTSTATUS status;
285         struct autorid_range_config range;
286
287         ZERO_STRUCT(range);
288         fstrcpy(range.domsid, domsid);
289         range.domain_range_index = domain_range_index;
290         range.rangenum = rangenum;
291
292         status = idmap_autorid_addrange(db, &range, false);
293         return status;
294 }
295
296 static NTSTATUS idmap_autorid_acquire_range(struct db_context *db,
297                                             struct autorid_range_config *range)
298 {
299         return idmap_autorid_addrange(db, range, true);
300 }
301
302 static NTSTATUS idmap_autorid_getrange_int(struct db_context *db,
303                                            struct autorid_range_config *range)
304 {
305         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
306         struct autorid_global_config *globalcfg = NULL;
307         fstring keystr;
308
309         if (db == NULL || range == NULL) {
310                 DEBUG(3, ("Invalid arguments received\n"));
311                 goto done;
312         }
313
314         if (!idmap_autorid_validate_sid(range->domsid)) {
315                 DEBUG(3, ("Invalid SID: '%s'\n", range->domsid));
316                 status = NT_STATUS_INVALID_PARAMETER;
317                 goto done;
318         }
319
320         idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
321                                    keystr);
322
323         DEBUG(10, ("reading domain range for key %s\n", keystr));
324         status = dbwrap_fetch_uint32_bystring(db, keystr, &(range->rangenum));
325         if (!NT_STATUS_IS_OK(status)) {
326                 DEBUG(1, ("Failed to read database for key '%s': %s\n",
327                           keystr, nt_errstr(status)));
328                 goto done;
329         }
330
331         status = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
332         if (!NT_STATUS_IS_OK(status)) {
333                 DEBUG(1, ("Failed to read global configuration"));
334                 goto done;
335         }
336         range->low_id = globalcfg->minvalue
337                       + range->rangenum * globalcfg->rangesize;
338
339         TALLOC_FREE(globalcfg);
340 done:
341         return status;
342 }
343
344 NTSTATUS idmap_autorid_getrange(struct db_context *db,
345                                 const char *domsid,
346                                 uint32_t domain_range_index,
347                                 uint32_t *rangenum,
348                                 uint32_t *low_id)
349 {
350         NTSTATUS status;
351         struct autorid_range_config range;
352
353         if (rangenum == NULL) {
354                 return NT_STATUS_INVALID_PARAMETER;
355         }
356
357         ZERO_STRUCT(range);
358         fstrcpy(range.domsid, domsid);
359         range.domain_range_index = domain_range_index;
360
361         status = idmap_autorid_getrange_int(db, &range);
362         if (!NT_STATUS_IS_OK(status)) {
363                 return status;
364         }
365
366         *rangenum = range.rangenum;
367
368         if (low_id != NULL) {
369                 *low_id = range.low_id;
370         }
371
372         return NT_STATUS_OK;
373 }
374
375 NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
376                                        struct autorid_range_config *range,
377                                        bool read_only)
378 {
379         NTSTATUS ret;
380
381         ret = idmap_autorid_getrange_int(db, range);
382         if (!NT_STATUS_IS_OK(ret)) {
383                 if (read_only) {
384                         return NT_STATUS_NOT_FOUND;
385                 }
386
387                 ret = idmap_autorid_acquire_range(db, range);
388         }
389
390         DEBUG(10, ("Using range #%d for domain %s "
391                    "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
392                    range->rangenum, range->domsid, range->domain_range_index,
393                    range->low_id));
394
395         return ret;
396 }
397
398 /* initialize the given HWM to 0 if it does not exist yet */
399 static NTSTATUS idmap_autorid_init_hwm_action(struct db_context *db,
400                                               void *private_data)
401 {
402         NTSTATUS status;
403         uint32_t hwmval;
404         const char *hwm;
405
406         hwm = (char *)private_data;
407
408         status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
409         if (NT_STATUS_IS_OK(status)) {
410                 DEBUG(1, ("HWM (%s) already initialized in autorid database "
411                           "(value %"PRIu32").\n", hwm, hwmval));
412                 return NT_STATUS_OK;
413         }
414         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
415                 DEBUG(0, ("Error fetching HWM (%s) from autorid "
416                           "database: %s\n", hwm, nt_errstr(status)));
417                 return status;
418         }
419
420         status = dbwrap_trans_store_uint32_bystring(db, hwm, 0);
421         if (!NT_STATUS_IS_OK(status)) {
422                 DEBUG(0, ("Error storing HWM (%s) in autorid database: %s\n",
423                           hwm, nt_errstr(status)));
424                 return status;
425         }
426
427         return NT_STATUS_OK;
428 }
429
430 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
431 {
432         NTSTATUS status;
433         uint32_t hwmval;
434
435         status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
436         if (NT_STATUS_IS_OK(status)) {
437                 DEBUG(1, ("HWM (%s) already initialized in autorid database "
438                           "(value %"PRIu32").\n", hwm, hwmval));
439                 return NT_STATUS_OK;
440         }
441         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
442                 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
443                           "database: %s\n", hwm,  nt_errstr(status)));
444                 return status;
445         }
446
447         status = dbwrap_trans_do(db, idmap_autorid_init_hwm_action,
448                                  (void *)hwm);
449         if (!NT_STATUS_IS_OK(status)) {
450                 DEBUG(0, ("Error initializing HWM (%s) in autorid database: "
451                           "%s\n", hwm, nt_errstr(status)));
452                 return NT_STATUS_INTERNAL_DB_ERROR;
453         }
454
455         DEBUG(1, ("Initialized HWM (%s) in autorid database.\n", hwm));
456
457         return NT_STATUS_OK;
458 }
459
460 /*
461  * Delete a domain#index <-> range mapping from the database.
462  * The mapping is specified by the sid and index.
463  * If force == true, invalid mapping records are deleted as far
464  * as possible, otherwise they are left untouched.
465  */
466
467 struct idmap_autorid_delete_range_by_sid_ctx {
468         const char *domsid;
469         uint32_t domain_range_index;
470         bool force;
471 };
472
473 static NTSTATUS idmap_autorid_delete_range_by_sid_action(struct db_context *db,
474                                                          void *private_data)
475 {
476         struct idmap_autorid_delete_range_by_sid_ctx *ctx =
477                 (struct idmap_autorid_delete_range_by_sid_ctx *)private_data;
478         const char *domsid;
479         uint32_t domain_range_index;
480         uint32_t rangenum;
481         char *keystr;
482         char *range_keystr;
483         TDB_DATA data;
484         NTSTATUS status;
485         TALLOC_CTX *frame = talloc_stackframe();
486         bool is_valid_range_mapping = true;
487         bool force;
488
489         domsid = ctx->domsid;
490         domain_range_index = ctx->domain_range_index;
491         force = ctx->force;
492
493         keystr = idmap_autorid_build_keystr_talloc(frame, domsid,
494                                                    domain_range_index);
495         if (keystr == NULL) {
496                 status = NT_STATUS_NO_MEMORY;
497                 goto done;
498         }
499
500         status = dbwrap_fetch_uint32_bystring(db, keystr, &rangenum);
501         if (!NT_STATUS_IS_OK(status)) {
502                 goto done;
503         }
504
505         range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
506         if (range_keystr == NULL) {
507                 status = NT_STATUS_NO_MEMORY;
508                 goto done;
509         }
510
511         status = dbwrap_fetch_bystring(db, frame, range_keystr, &data);
512         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
513                 DEBUG(1, ("Incomplete mapping %s -> %s: no backward mapping\n",
514                           keystr, range_keystr));
515                 is_valid_range_mapping = false;
516         } else if (!NT_STATUS_IS_OK(status)) {
517                 DEBUG(1, ("Error fetching reverse mapping for %s -> %s:  %s\n",
518                           keystr, range_keystr, nt_errstr(status)));
519                 goto done;
520         } else if (strncmp((const char *)data.dptr, keystr, strlen(keystr))
521                    != 0)
522         {
523                 DEBUG(1, ("Invalid mapping: %s -> %s -> %s\n",
524                           keystr, range_keystr, (const char *)data.dptr));
525                 is_valid_range_mapping = false;
526         }
527
528         if (!is_valid_range_mapping && !force) {
529                 DEBUG(10, ("Not deleting invalid mapping, since not in force "
530                            "mode.\n"));
531                 status = NT_STATUS_FILE_INVALID;
532                 goto done;
533         }
534
535         status = dbwrap_delete_bystring(db, keystr);
536         if (!NT_STATUS_IS_OK(status)) {
537                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
538                           keystr, nt_errstr(status)));
539                 goto done;
540         }
541
542         if (!is_valid_range_mapping) {
543                 goto done;
544         }
545
546         status = dbwrap_delete_bystring(db, range_keystr);
547         if (!NT_STATUS_IS_OK(status)) {
548                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
549                           range_keystr, nt_errstr(status)));
550                 goto done;
551         }
552
553         DEBUG(10, ("Deleted range mapping %s <--> %s\n", keystr,
554                    range_keystr));
555
556 done:
557         TALLOC_FREE(frame);
558         return status;
559 }
560
561 NTSTATUS idmap_autorid_delete_range_by_sid(struct db_context *db,
562                                            const char *domsid,
563                                            uint32_t domain_range_index,
564                                            bool force)
565 {
566         NTSTATUS status;
567         struct idmap_autorid_delete_range_by_sid_ctx ctx;
568
569         ctx.domain_range_index = domain_range_index;
570         ctx.domsid = domsid;
571         ctx.force = force;
572
573         status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_sid_action,
574                                  &ctx);
575         return status;
576 }
577
578 /*
579  * Delete a domain#index <-> range mapping from the database.
580  * The mapping is specified by the range number.
581  * If force == true, invalid mapping records are deleted as far
582  * as possible, otherwise they are left untouched.
583  */
584 struct idmap_autorid_delete_range_by_num_ctx {
585         uint32_t rangenum;
586         bool force;
587 };
588
589 static NTSTATUS idmap_autorid_delete_range_by_num_action(struct db_context *db,
590                                                            void *private_data)
591 {
592         struct idmap_autorid_delete_range_by_num_ctx *ctx =
593                 (struct idmap_autorid_delete_range_by_num_ctx *)private_data;
594         uint32_t rangenum;
595         char *keystr;
596         char *range_keystr;
597         TDB_DATA val;
598         NTSTATUS status;
599         TALLOC_CTX *frame = talloc_stackframe();
600         bool is_valid_range_mapping = true;
601         bool force;
602
603         rangenum = ctx->rangenum;
604         force = ctx->force;
605
606         range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
607         if (range_keystr == NULL) {
608                 status = NT_STATUS_NO_MEMORY;
609                 goto done;
610         }
611
612         ZERO_STRUCT(val);
613
614         status = dbwrap_fetch_bystring(db, frame, range_keystr, &val);
615         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
616                 DEBUG(10, ("Did not find range '%s' in database.\n",
617                            range_keystr));
618                 goto done;
619         } else if (!NT_STATUS_IS_OK(status)) {
620                 DEBUG(5, ("Error fetching rang key: %s\n", nt_errstr(status)));
621                 goto done;
622         }
623
624         if (val.dptr == NULL) {
625                 DEBUG(1, ("Invalid mapping: %s -> empty value\n",
626                           range_keystr));
627                 is_valid_range_mapping = false;
628         } else {
629                 uint32_t reverse_rangenum = 0;
630
631                 keystr = (char *)val.dptr;
632
633                 status = dbwrap_fetch_uint32_bystring(db, keystr,
634                                                       &reverse_rangenum);
635                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
636                         DEBUG(1, ("Incomplete mapping %s -> %s: "
637                                   "no backward mapping\n",
638                                   range_keystr, keystr));
639                         is_valid_range_mapping = false;
640                 } else if (!NT_STATUS_IS_OK(status)) {
641                         DEBUG(1, ("Error fetching reverse mapping for "
642                                   "%s -> %s: %s\n",
643                                   range_keystr, keystr, nt_errstr(status)));
644                         goto done;
645                 } else if (rangenum != reverse_rangenum) {
646                         is_valid_range_mapping = false;
647                 }
648         }
649
650         if (!is_valid_range_mapping && !force) {
651                 DEBUG(10, ("Not deleting invalid mapping, since not in force "
652                            "mode.\n"));
653                 status = NT_STATUS_FILE_INVALID;
654                 goto done;
655         }
656
657         status = dbwrap_delete_bystring(db, range_keystr);
658         if (!NT_STATUS_IS_OK(status)) {
659                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
660                           range_keystr, nt_errstr(status)));
661                 goto done;
662         }
663
664         if (!is_valid_range_mapping) {
665                 goto done;
666         }
667
668         status = dbwrap_delete_bystring(db, keystr);
669         if (!NT_STATUS_IS_OK(status)) {
670                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
671                           keystr, nt_errstr(status)));
672                 goto done;
673         }
674
675         DEBUG(10, ("Deleted range mapping %s <--> %s\n", range_keystr,
676                    keystr));
677
678 done:
679         talloc_free(frame);
680         return status;
681 }
682
683 NTSTATUS idmap_autorid_delete_range_by_num(struct db_context *db,
684                                            uint32_t rangenum,
685                                            bool force)
686 {
687         NTSTATUS status;
688         struct idmap_autorid_delete_range_by_num_ctx ctx;
689
690         ctx.rangenum = rangenum;
691         ctx.force = force;
692
693         status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_num_action,
694                                  &ctx);
695         return status;
696 }
697
698 /*
699  * open and initialize the database which stores the ranges for the domains
700  */
701 NTSTATUS idmap_autorid_db_init(const char *path,
702                                TALLOC_CTX *mem_ctx,
703                                struct db_context **db)
704 {
705         NTSTATUS status;
706
707         if (*db != NULL) {
708                 /* its already open */
709                 return NT_STATUS_OK;
710         }
711
712         /* Open idmap repository */
713         *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
714                       DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
715
716         if (*db == NULL) {
717                 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
718                 return NT_STATUS_UNSUCCESSFUL;
719         }
720
721         /* Initialize high water mark for the currently used range to 0 */
722
723         status = idmap_autorid_init_hwm(*db, HWM);
724         NT_STATUS_NOT_OK_RETURN(status);
725
726         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
727         NT_STATUS_NOT_OK_RETURN(status);
728
729         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
730
731         return status;
732 }
733
734 struct idmap_autorid_fetch_config_state {
735         TALLOC_CTX *mem_ctx;
736         char *configstr;
737 };
738
739 static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
740                                         void *private_data)
741 {
742         struct idmap_autorid_fetch_config_state *state;
743
744         state = (struct idmap_autorid_fetch_config_state *)private_data;
745
746         /*
747          * strndup because we have non-nullterminated strings in the db
748          */
749         state->configstr = talloc_strndup(
750                 state->mem_ctx, (const char *)value.dptr, value.dsize);
751 }
752
753 NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
754                                     char **result)
755 {
756         TDB_DATA key;
757         NTSTATUS status;
758         struct idmap_autorid_fetch_config_state state;
759
760         if (result == NULL) {
761                 return NT_STATUS_INVALID_PARAMETER;
762         }
763
764         key = string_term_tdb_data(CONFIGKEY);
765
766         state.mem_ctx = mem_ctx;
767         state.configstr = NULL;
768
769         status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
770                                      &state);
771         if (!NT_STATUS_IS_OK(status)) {
772                 DEBUG(1, ("Error while retrieving config: %s\n",
773                           nt_errstr(status)));
774                 return status;
775         }
776
777         if (state.configstr == NULL) {
778                 DEBUG(1, ("Error while retrieving config\n"));
779                 return NT_STATUS_NO_MEMORY;
780         }
781
782         DEBUG(5, ("found CONFIG: %s\n", state.configstr));
783
784         *result = state.configstr;
785         return NT_STATUS_OK;
786 }
787
788 bool idmap_autorid_parse_configstr(const char *configstr,
789                                    struct autorid_global_config *cfg)
790 {
791         unsigned long minvalue, rangesize, maxranges;
792
793         if (sscanf(configstr,
794                    "minvalue:%lu rangesize:%lu maxranges:%lu",
795                    &minvalue, &rangesize, &maxranges) != 3) {
796                 DEBUG(1,
797                       ("Found invalid configuration data. "
798                        "Creating new config\n"));
799                 return false;
800         }
801
802         cfg->minvalue = minvalue;
803         cfg->rangesize = rangesize;
804         cfg->maxranges = maxranges;
805
806         return true;
807 }
808
809 NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
810                                   TALLOC_CTX *mem_ctx,
811                                   struct autorid_global_config **result)
812 {
813         struct autorid_global_config *cfg;
814         NTSTATUS status;
815         bool ok;
816         char *configstr = NULL;
817
818         if (result == NULL) {
819                 return NT_STATUS_INVALID_PARAMETER;
820         }
821
822         status = idmap_autorid_getconfigstr(db, mem_ctx, &configstr);
823         if (!NT_STATUS_IS_OK(status)) {
824                 return status;
825         }
826
827         cfg = talloc_zero(mem_ctx, struct autorid_global_config);
828         if (cfg == NULL) {
829                 return NT_STATUS_NO_MEMORY;
830         }
831
832         ok = idmap_autorid_parse_configstr(configstr, cfg);
833         if (!ok) {
834                 talloc_free(cfg);
835                 return NT_STATUS_INVALID_PARAMETER;
836         }
837
838         DEBUG(10, ("Loaded previously stored configuration "
839                    "minvalue:%d rangesize:%d\n",
840                    cfg->minvalue, cfg->rangesize));
841
842         *result = cfg;
843
844         return NT_STATUS_OK;
845 }
846
847 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
848                                   struct autorid_global_config *cfg)
849 {
850
851         struct autorid_global_config *storedconfig = NULL;
852         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
853         TDB_DATA data;
854         char *cfgstr;
855         uint32_t hwm;
856         TALLOC_CTX *frame = talloc_stackframe();
857
858         DEBUG(10, ("New configuration provided for storing is "
859                    "minvalue:%d rangesize:%d maxranges:%d\n",
860                    cfg->minvalue, cfg->rangesize, cfg->maxranges));
861
862         if (cfg->rangesize < 2000) {
863                 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
864                 goto done;
865         }
866
867         if (cfg->maxranges == 0) {
868                 DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
869                           "Must have at least one range available.\n"));
870                 goto done;
871         }
872
873         status = idmap_autorid_loadconfig(db, frame, &storedconfig);
874         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
875                 DEBUG(5, ("No configuration found. Storing initial "
876                           "configuration.\n"));
877         } else if (!NT_STATUS_IS_OK(status)) {
878                 goto done;
879         }
880
881         /* did the minimum value or rangesize change? */
882         if (storedconfig &&
883             ((storedconfig->minvalue != cfg->minvalue) ||
884              (storedconfig->rangesize != cfg->rangesize)))
885         {
886                 DEBUG(1, ("New configuration values for rangesize or "
887                           "minimum uid value conflict with previously "
888                           "used values! Not storing new config.\n"));
889                 status = NT_STATUS_INVALID_PARAMETER;
890                 goto done;
891         }
892
893         status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
894         if (!NT_STATUS_IS_OK(status)) {
895                 DEBUG(1, ("Fatal error while fetching current "
896                           "HWM value: %s\n", nt_errstr(status)));
897                 status = NT_STATUS_INTERNAL_ERROR;
898                 goto done;
899         }
900
901         /*
902          * has the highest uid value been reduced to setting that is not
903          * sufficient any more for already existing ranges?
904          */
905         if (hwm > cfg->maxranges) {
906                 DEBUG(1, ("New upper uid limit is too low to cover "
907                           "existing mappings! Not storing new config.\n"));
908                 status = NT_STATUS_INVALID_PARAMETER;
909                 goto done;
910         }
911
912         cfgstr =
913             talloc_asprintf(frame,
914                             "minvalue:%u rangesize:%u maxranges:%u",
915                             cfg->minvalue, cfg->rangesize, cfg->maxranges);
916
917         if (cfgstr == NULL) {
918                 status = NT_STATUS_NO_MEMORY;
919                 goto done;
920         }
921
922         data = string_tdb_data(cfgstr);
923
924         status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);
925
926 done:
927         TALLOC_FREE(frame);
928         return status;
929 }
930
931 NTSTATUS idmap_autorid_saveconfigstr(struct db_context *db,
932                                      const char *configstr)
933 {
934         bool ok;
935         NTSTATUS status;
936         struct autorid_global_config cfg;
937
938         ok = idmap_autorid_parse_configstr(configstr, &cfg);
939         if (!ok) {
940                 return NT_STATUS_INVALID_PARAMETER;
941         }
942
943         status = idmap_autorid_saveconfig(db, &cfg);
944         return status;
945 }
946
947
948 /*
949  * iteration: Work on all range mappings for a given domain
950  */
951
952 struct domain_range_visitor_ctx {
953         const char *domsid;
954         NTSTATUS (*fn)(struct db_context *db,
955                        const char *domsid,
956                        uint32_t index,
957                        uint32_t rangenum,
958                        void *private_data);
959         void *private_data;
960         int count; /* number of records worked on */
961 };
962
963 static int idmap_autorid_visit_domain_range(struct db_record *rec,
964                                             void *private_data)
965 {
966         struct domain_range_visitor_ctx *vi;
967         char *domsid;
968         char *sep;
969         uint32_t range_index = 0;
970         uint32_t rangenum = 0;
971         TDB_DATA key, value;
972         NTSTATUS status;
973         int ret = 0;
974         struct db_context *db;
975
976         vi = talloc_get_type_abort(private_data,
977                                    struct domain_range_visitor_ctx);
978
979         key = dbwrap_record_get_key(rec);
980
981         /*
982          * split string "<sid>[#<index>]" into sid string and index number
983          */
984
985         domsid = (char *)key.dptr;
986
987         DEBUG(10, ("idmap_autorid_visit_domain_range: visiting key '%s'\n",
988                    domsid));
989
990         sep = strrchr(domsid, '#');
991         if (sep != NULL) {
992                 char *index_str;
993                 *sep = '\0';
994                 index_str = sep+1;
995                 if (sscanf(index_str, "%"SCNu32, &range_index) != 1) {
996                         DEBUG(10, ("Found separator '#' but '%s' is not a "
997                                    "valid range index. Skipping record\n",
998                                    index_str));
999                         goto done;
1000                 }
1001         }
1002
1003         if (!idmap_autorid_validate_sid(domsid)) {
1004                 DEBUG(10, ("String '%s' is not a valid sid. "
1005                            "Skipping record.\n", domsid));
1006                 goto done;
1007         }
1008
1009         if ((vi->domsid != NULL) && (strcmp(domsid, vi->domsid) != 0)) {
1010                 DEBUG(10, ("key sid '%s' does not match requested sid '%s'.\n",
1011                            domsid, vi->domsid));
1012                 goto done;
1013         }
1014
1015         value = dbwrap_record_get_value(rec);
1016
1017         if (value.dsize != sizeof(uint32_t)) {
1018                 /* it might be a mapping of a well known sid */
1019                 DEBUG(10, ("value size %u != sizeof(uint32_t) for sid '%s', "
1020                            "skipping.\n", (unsigned)value.dsize, vi->domsid));
1021                 goto done;
1022         }
1023
1024         rangenum = IVAL(value.dptr, 0);
1025
1026         db = dbwrap_record_get_db(rec);
1027
1028         status = vi->fn(db, domsid, range_index, rangenum, vi->private_data);
1029         if (!NT_STATUS_IS_OK(status)) {
1030                 ret = -1;
1031                 goto done;
1032         }
1033
1034         vi->count++;
1035         ret = 0;
1036
1037 done:
1038         return ret;
1039 }
1040
1041 static NTSTATUS idmap_autorid_iterate_domain_ranges_int(struct db_context *db,
1042                                 const char *domsid,
1043                                 NTSTATUS (*fn)(struct db_context *db,
1044                                                const char *domsid,
1045                                                uint32_t index,
1046                                                uint32_t rangnum,
1047                                                void *private_data),
1048                                 void *private_data,
1049                                 int *count,
1050                                 NTSTATUS (*traverse)(struct db_context *db,
1051                                           int (*f)(struct db_record *, void *),
1052                                           void *private_data,
1053                                           int *count))
1054 {
1055         NTSTATUS status;
1056         struct domain_range_visitor_ctx *vi;
1057         TALLOC_CTX *frame = talloc_stackframe();
1058
1059         if (domsid == NULL) {
1060                 DEBUG(10, ("No sid provided, operating on all ranges\n"));
1061         }
1062
1063         if (fn == NULL) {
1064                 DEBUG(1, ("Error: missing visitor callback\n"));
1065                 status = NT_STATUS_INVALID_PARAMETER;
1066                 goto done;
1067         }
1068
1069         vi = talloc_zero(frame, struct domain_range_visitor_ctx);
1070         if (vi == NULL) {
1071                 status = NT_STATUS_NO_MEMORY;
1072                 goto done;
1073         }
1074
1075         vi->domsid = domsid;
1076         vi->fn = fn;
1077         vi->private_data = private_data;
1078
1079         status = traverse(db, idmap_autorid_visit_domain_range, vi, NULL);
1080         if (!NT_STATUS_IS_OK(status)) {
1081                 goto done;
1082         }
1083
1084         if (count != NULL) {
1085                 *count = vi->count;
1086         }
1087
1088 done:
1089         talloc_free(frame);
1090         return status;
1091 }
1092
1093 NTSTATUS idmap_autorid_iterate_domain_ranges(struct db_context *db,
1094                                         const char *domsid,
1095                                         NTSTATUS (*fn)(struct db_context *db,
1096                                                        const char *domsid,
1097                                                        uint32_t index,
1098                                                        uint32_t rangenum,
1099                                                        void *private_data),
1100                                         void *private_data,
1101                                         int *count)
1102 {
1103         NTSTATUS status;
1104
1105         status = idmap_autorid_iterate_domain_ranges_int(db,
1106                                                          domsid,
1107                                                          fn,
1108                                                          private_data,
1109                                                          count,
1110                                                          dbwrap_traverse);
1111
1112         return status;
1113 }
1114
1115
1116 NTSTATUS idmap_autorid_iterate_domain_ranges_read(struct db_context *db,
1117                                         const char *domsid,
1118                                         NTSTATUS (*fn)(struct db_context *db,
1119                                                        const char *domsid,
1120                                                        uint32_t index,
1121                                                        uint32_t rangenum,
1122                                                        void *count),
1123                                         void *private_data,
1124                                         int *count)
1125 {
1126         NTSTATUS status;
1127
1128         status = idmap_autorid_iterate_domain_ranges_int(db,
1129                                                          domsid,
1130                                                          fn,
1131                                                          private_data,
1132                                                          count,
1133                                                          dbwrap_traverse_read);
1134
1135         return status;
1136 }
1137
1138
1139 /*
1140  * Delete all ranges configured for a given domain
1141  */
1142
1143 struct delete_domain_ranges_visitor_ctx {
1144         bool force;
1145 };
1146
1147 static NTSTATUS idmap_autorid_delete_domain_ranges_visitor(
1148                                                 struct db_context *db,
1149                                                 const char *domsid,
1150                                                 uint32_t domain_range_index,
1151                                                 uint32_t rangenum,
1152                                                 void *private_data)
1153 {
1154         struct delete_domain_ranges_visitor_ctx *ctx;
1155         NTSTATUS status;
1156
1157         ctx = (struct delete_domain_ranges_visitor_ctx *)private_data;
1158
1159         status = idmap_autorid_delete_range_by_sid(
1160                                 db, domsid, domain_range_index, ctx->force);
1161         return status;
1162 }
1163
1164 struct idmap_autorid_delete_domain_ranges_ctx {
1165         const char *domsid;
1166         bool force;
1167         int count; /* output: count records operated on */
1168 };
1169
1170 static NTSTATUS idmap_autorid_delete_domain_ranges_action(struct db_context *db,
1171                                                           void *private_data)
1172 {
1173         struct idmap_autorid_delete_domain_ranges_ctx *ctx;
1174         struct delete_domain_ranges_visitor_ctx visitor_ctx;
1175         int count;
1176         NTSTATUS status;
1177
1178         ctx = (struct idmap_autorid_delete_domain_ranges_ctx *)private_data;
1179
1180         ZERO_STRUCT(visitor_ctx);
1181         visitor_ctx.force = ctx->force;
1182
1183         status = idmap_autorid_iterate_domain_ranges(db,
1184                                 ctx->domsid,
1185                                 idmap_autorid_delete_domain_ranges_visitor,
1186                                 &visitor_ctx,
1187                                 &count);
1188         if (!NT_STATUS_IS_OK(status)) {
1189                 return status;
1190         }
1191
1192         ctx->count = count;
1193
1194         return NT_STATUS_OK;
1195 }
1196
1197 NTSTATUS idmap_autorid_delete_domain_ranges(struct db_context *db,
1198                                             const char *domsid,
1199                                             bool force,
1200                                             int *count)
1201 {
1202         NTSTATUS status;
1203         struct idmap_autorid_delete_domain_ranges_ctx ctx;
1204
1205         ZERO_STRUCT(ctx);
1206         ctx.domsid = domsid;
1207         ctx.force = force;
1208
1209         status = dbwrap_trans_do(db, idmap_autorid_delete_domain_ranges_action,
1210                                  &ctx);
1211         if (!NT_STATUS_IS_OK(status)) {
1212                 return status;
1213         }
1214
1215         *count = ctx.count;
1216
1217         return NT_STATUS_OK;
1218 }