91909cd17b0bbe99976f39ee7bd184da5e1eca4a
[mat/samba.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         if (requested_rangenum < hwm) {
179                 /*
180                  * Set a specified range below the HWM:
181                  * We need to check that it is not yet taken.
182                  */
183
184                 numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
185                 if (!numstr) {
186                         ret = NT_STATUS_NO_MEMORY;
187                         goto error;
188                 }
189
190                 if (dbwrap_exists(db, string_term_tdb_data(numstr))) {
191                         DEBUG(1, ("Requested range already in use.\n"));
192                         ret = NT_STATUS_INVALID_PARAMETER;
193                         goto error;
194                 }
195
196                 TALLOC_FREE(numstr);
197         } else {
198                 /*
199                  * requested or automatic range >= HWM:
200                  * increment the HWM.
201                  */
202
203                 /* HWM always contains current max range + 1 */
204                 increment = requested_rangenum + 1 - hwm;
205
206                 /* increase the HWM */
207                 ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &hwm,
208                                                            increment);
209                 if (!NT_STATUS_IS_OK(ret)) {
210                         DEBUG(1, ("Fatal error while incrementing the HWM "
211                                   "value in the database: %s\n",
212                                   nt_errstr(ret)));
213                         goto error;
214                 }
215         }
216
217         /*
218          * store away the new mapping in both directions
219          */
220
221         ret = dbwrap_store_uint32_bystring(db, keystr, requested_rangenum);
222         if (!NT_STATUS_IS_OK(ret)) {
223                 DEBUG(1, ("Fatal error while storing new "
224                           "domain->range assignment: %s\n", nt_errstr(ret)));
225                 goto error;
226         }
227
228         numstr = talloc_asprintf(mem_ctx, "%u", requested_rangenum);
229         if (!numstr) {
230                 ret = NT_STATUS_NO_MEMORY;
231                 goto error;
232         }
233
234         ret = dbwrap_store_bystring(db, numstr,
235                         string_term_tdb_data(keystr), TDB_INSERT);
236
237         if (!NT_STATUS_IS_OK(ret)) {
238                 DEBUG(1, ("Fatal error while storing new "
239                           "domain->range assignment: %s\n", nt_errstr(ret)));
240                 goto error;
241         }
242         DEBUG(5, ("Acquired new range #%d for domain %s "
243                   "(domain_range_index=%"PRIu32")\n", requested_rangenum, keystr,
244                   range->domain_range_index));
245
246         range->rangenum = requested_rangenum;
247
248         range->low_id = globalcfg->minvalue
249                       + range->rangenum * globalcfg->rangesize;
250
251         ret = NT_STATUS_OK;
252
253 error:
254         talloc_free(mem_ctx);
255         return ret;
256 }
257
258 static NTSTATUS idmap_autorid_addrange(struct db_context *db,
259                                        struct autorid_range_config *range,
260                                        bool acquire)
261 {
262         NTSTATUS status;
263         struct idmap_autorid_addrange_ctx ctx;
264
265         ctx.acquire = acquire;
266         ctx.range = range;
267
268         status = dbwrap_trans_do(db, idmap_autorid_addrange_action, &ctx);
269         return status;
270 }
271
272 NTSTATUS idmap_autorid_setrange(struct db_context *db,
273                                 const char *domsid,
274                                 uint32_t domain_range_index,
275                                 uint32_t rangenum)
276 {
277         NTSTATUS status;
278         struct autorid_range_config range;
279
280         ZERO_STRUCT(range);
281         fstrcpy(range.domsid, domsid);
282         range.domain_range_index = domain_range_index;
283         range.rangenum = rangenum;
284
285         status = idmap_autorid_addrange(db, &range, false);
286         return status;
287 }
288
289 static NTSTATUS idmap_autorid_acquire_range(struct db_context *db,
290                                             struct autorid_range_config *range)
291 {
292         return idmap_autorid_addrange(db, range, true);
293 }
294
295 static NTSTATUS idmap_autorid_getrange_int(struct db_context *db,
296                                            struct autorid_range_config *range)
297 {
298         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
299         struct autorid_global_config *globalcfg = NULL;
300         fstring keystr;
301
302         if (db == NULL || range == NULL) {
303                 DEBUG(3, ("Invalid arguments received\n"));
304                 goto done;
305         }
306
307         idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
308                                    keystr);
309
310         DEBUG(10, ("reading domain range for key %s\n", keystr));
311         status = dbwrap_fetch_uint32_bystring(db, keystr, &(range->rangenum));
312         if (!NT_STATUS_IS_OK(status)) {
313                 DEBUG(1, ("Failed to read database for key '%s': %s\n",
314                           keystr, nt_errstr(status)));
315                 goto done;
316         }
317
318         status = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
319         if (!NT_STATUS_IS_OK(status)) {
320                 DEBUG(1, ("Failed to read global configuration"));
321                 goto done;
322         }
323         range->low_id = globalcfg->minvalue
324                       + range->rangenum * globalcfg->rangesize;
325
326         TALLOC_FREE(globalcfg);
327 done:
328         return status;
329 }
330
331 NTSTATUS idmap_autorid_getrange(struct db_context *db,
332                                 const char *domsid,
333                                 uint32_t domain_range_index,
334                                 uint32_t *rangenum,
335                                 uint32_t *low_id)
336 {
337         NTSTATUS status;
338         struct autorid_range_config range;
339
340         if (rangenum == NULL) {
341                 return NT_STATUS_INVALID_PARAMETER;
342         }
343
344         ZERO_STRUCT(range);
345         fstrcpy(range.domsid, domsid);
346         range.domain_range_index = domain_range_index;
347
348         status = idmap_autorid_getrange_int(db, &range);
349         if (!NT_STATUS_IS_OK(status)) {
350                 return status;
351         }
352
353         *rangenum = range.rangenum;
354
355         if (low_id != NULL) {
356                 *low_id = range.low_id;
357         }
358
359         return NT_STATUS_OK;
360 }
361
362 NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
363                                        struct autorid_range_config *range,
364                                        bool read_only)
365 {
366         NTSTATUS ret;
367
368         ret = idmap_autorid_getrange_int(db, range);
369         if (!NT_STATUS_IS_OK(ret)) {
370                 if (read_only) {
371                         return NT_STATUS_NOT_FOUND;
372                 }
373
374                 ret = idmap_autorid_acquire_range(db, range);
375         }
376
377         DEBUG(10, ("Using range #%d for domain %s "
378                    "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
379                    range->rangenum, range->domsid, range->domain_range_index,
380                    range->low_id));
381
382         return ret;
383 }
384
385 /* initialize the given HWM to 0 if it does not exist yet */
386 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
387 {
388         NTSTATUS status;
389         uint32_t hwmval;
390
391         status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
392         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND))  {
393                 status = dbwrap_trans_store_int32_bystring(db, hwm, 0);
394                 if (!NT_STATUS_IS_OK(status)) {
395                         DEBUG(0,
396                               ("Unable to initialise HWM (%s) in autorid "
397                                "database: %s\n", hwm, nt_errstr(status)));
398                         return NT_STATUS_INTERNAL_DB_ERROR;
399                 }
400         } else if (!NT_STATUS_IS_OK(status)) {
401                 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
402                           "database: %s\n", hwm,  nt_errstr(status)));
403                 return status;
404         }
405
406         return NT_STATUS_OK;
407 }
408
409 /*
410  * Delete a domain#index <-> range mapping from the database.
411  * The mapping is specified by the sid and index.
412  * If force == true, invalid mapping records are deleted as far
413  * as possible, otherwise they are left untouched.
414  */
415
416 struct idmap_autorid_delete_range_by_sid_ctx {
417         const char *domsid;
418         uint32_t domain_range_index;
419         bool force;
420 };
421
422 static NTSTATUS idmap_autorid_delete_range_by_sid_action(struct db_context *db,
423                                                          void *private_data)
424 {
425         struct idmap_autorid_delete_range_by_sid_ctx *ctx =
426                 (struct idmap_autorid_delete_range_by_sid_ctx *)private_data;
427         const char *domsid;
428         uint32_t domain_range_index;
429         uint32_t rangenum;
430         char *keystr;
431         char *range_keystr;
432         TDB_DATA data;
433         NTSTATUS status;
434         TALLOC_CTX *frame = talloc_stackframe();
435         bool is_valid_range_mapping = true;
436         bool force;
437
438         domsid = ctx->domsid;
439         domain_range_index = ctx->domain_range_index;
440         force = ctx->force;
441
442         keystr = idmap_autorid_build_keystr_talloc(frame, domsid,
443                                                    domain_range_index);
444         if (keystr == NULL) {
445                 status = NT_STATUS_NO_MEMORY;
446                 goto done;
447         }
448
449         status = dbwrap_fetch_uint32_bystring(db, keystr, &rangenum);
450         if (!NT_STATUS_IS_OK(status)) {
451                 goto done;
452         }
453
454         range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
455         if (range_keystr == NULL) {
456                 status = NT_STATUS_NO_MEMORY;
457                 goto done;
458         }
459
460         status = dbwrap_fetch_bystring(db, frame, range_keystr, &data);
461         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
462                 DEBUG(1, ("Incomplete mapping %s -> %s: no backward mapping\n",
463                           keystr, range_keystr));
464                 is_valid_range_mapping = false;
465         } else if (!NT_STATUS_IS_OK(status)) {
466                 DEBUG(1, ("Error fetching reverse mapping for %s -> %s:  %s\n",
467                           keystr, range_keystr, nt_errstr(status)));
468                 goto done;
469         } else if (strncmp((const char *)data.dptr, keystr, strlen(keystr))
470                    != 0)
471         {
472                 DEBUG(1, ("Invalid mapping: %s -> %s -> %s\n",
473                           keystr, range_keystr, (const char *)data.dptr));
474                 is_valid_range_mapping = false;
475         }
476
477         if (!is_valid_range_mapping && !force) {
478                 DEBUG(10, ("Not deleting invalid mapping, since not in force "
479                            "mode.\n"));
480                 status = NT_STATUS_FILE_INVALID;
481                 goto done;
482         }
483
484         status = dbwrap_delete_bystring(db, keystr);
485         if (!NT_STATUS_IS_OK(status)) {
486                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
487                           keystr, nt_errstr(status)));
488                 goto done;
489         }
490
491         if (!is_valid_range_mapping) {
492                 goto done;
493         }
494
495         status = dbwrap_delete_bystring(db, range_keystr);
496         if (!NT_STATUS_IS_OK(status)) {
497                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
498                           range_keystr, nt_errstr(status)));
499                 goto done;
500         }
501
502         DEBUG(10, ("Deleted range mapping %s <--> %s\n", keystr,
503                    range_keystr));
504
505 done:
506         TALLOC_FREE(frame);
507         return status;
508 }
509
510 NTSTATUS idmap_autorid_delete_range_by_sid(struct db_context *db,
511                                            const char *domsid,
512                                            uint32_t domain_range_index,
513                                            bool force)
514 {
515         NTSTATUS status;
516         struct idmap_autorid_delete_range_by_sid_ctx ctx;
517
518         ctx.domain_range_index = domain_range_index;
519         ctx.domsid = domsid;
520         ctx.force = force;
521
522         status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_sid_action,
523                                  &ctx);
524         return status;
525 }
526
527 /*
528  * Delete a domain#index <-> range mapping from the database.
529  * The mapping is specified by the range number.
530  * If force == true, invalid mapping records are deleted as far
531  * as possible, otherwise they are left untouched.
532  */
533 struct idmap_autorid_delete_range_by_num_ctx {
534         uint32_t rangenum;
535         bool force;
536 };
537
538 static NTSTATUS idmap_autorid_delete_range_by_num_action(struct db_context *db,
539                                                            void *private_data)
540 {
541         struct idmap_autorid_delete_range_by_num_ctx *ctx =
542                 (struct idmap_autorid_delete_range_by_num_ctx *)private_data;
543         uint32_t rangenum;
544         char *keystr;
545         char *range_keystr;
546         TDB_DATA val;
547         NTSTATUS status;
548         TALLOC_CTX *frame = talloc_stackframe();
549         bool is_valid_range_mapping = true;
550         bool force;
551
552         rangenum = ctx->rangenum;
553         force = ctx->force;
554
555         range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
556         if (range_keystr == NULL) {
557                 status = NT_STATUS_NO_MEMORY;
558                 goto done;
559         }
560
561         ZERO_STRUCT(val);
562
563         status = dbwrap_fetch_bystring(db, frame, range_keystr, &val);
564         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
565                 DEBUG(10, ("Did not find range '%s' in database.\n",
566                            range_keystr));
567                 goto done;
568         } else if (!NT_STATUS_IS_OK(status)) {
569                 DEBUG(5, ("Error fetching rang key: %s\n", nt_errstr(status)));
570                 goto done;
571         }
572
573         if (val.dptr == NULL) {
574                 DEBUG(1, ("Invalid mapping: %s -> empty value\n",
575                           range_keystr));
576                 is_valid_range_mapping = false;
577         } else {
578                 uint32_t reverse_rangenum = 0;
579
580                 keystr = (char *)val.dptr;
581
582                 status = dbwrap_fetch_uint32_bystring(db, keystr,
583                                                       &reverse_rangenum);
584                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
585                         DEBUG(1, ("Incomplete mapping %s -> %s: "
586                                   "no backward mapping\n",
587                                   range_keystr, keystr));
588                         is_valid_range_mapping = false;
589                 } else if (!NT_STATUS_IS_OK(status)) {
590                         DEBUG(1, ("Error fetching reverse mapping for "
591                                   "%s -> %s: %s\n",
592                                   range_keystr, keystr, nt_errstr(status)));
593                         goto done;
594                 } else if (rangenum != reverse_rangenum) {
595                         is_valid_range_mapping = false;
596                 }
597         }
598
599         if (!is_valid_range_mapping && !force) {
600                 DEBUG(10, ("Not deleting invalid mapping, since not in force "
601                            "mode.\n"));
602                 status = NT_STATUS_FILE_INVALID;
603                 goto done;
604         }
605
606         status = dbwrap_delete_bystring(db, range_keystr);
607         if (!NT_STATUS_IS_OK(status)) {
608                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
609                           range_keystr, nt_errstr(status)));
610                 goto done;
611         }
612
613         if (!is_valid_range_mapping) {
614                 goto done;
615         }
616
617         status = dbwrap_delete_bystring(db, keystr);
618         if (!NT_STATUS_IS_OK(status)) {
619                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
620                           keystr, nt_errstr(status)));
621                 goto done;
622         }
623
624         DEBUG(10, ("Deleted range mapping %s <--> %s\n", range_keystr,
625                    keystr));
626
627 done:
628         talloc_free(frame);
629         return status;
630 }
631
632 NTSTATUS idmap_autorid_delete_range_by_num(struct db_context *db,
633                                            uint32_t rangenum,
634                                            bool force)
635 {
636         NTSTATUS status;
637         struct idmap_autorid_delete_range_by_num_ctx ctx;
638
639         ctx.rangenum = rangenum;
640         ctx.force = force;
641
642         status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_num_action,
643                                  &ctx);
644         return status;
645 }
646
647 /*
648  * open and initialize the database which stores the ranges for the domains
649  */
650 NTSTATUS idmap_autorid_db_init(const char *path,
651                                TALLOC_CTX *mem_ctx,
652                                struct db_context **db)
653 {
654         NTSTATUS status;
655
656         if (*db != NULL) {
657                 /* its already open */
658                 return NT_STATUS_OK;
659         }
660
661         /* Open idmap repository */
662         *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
663                       DBWRAP_LOCK_ORDER_1);
664
665         if (*db == NULL) {
666                 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
667                 return NT_STATUS_UNSUCCESSFUL;
668         }
669
670         /* Initialize high water mark for the currently used range to 0 */
671
672         status = idmap_autorid_init_hwm(*db, HWM);
673         NT_STATUS_NOT_OK_RETURN(status);
674
675         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
676         NT_STATUS_NOT_OK_RETURN(status);
677
678         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
679
680         return status;
681 }
682
683 struct idmap_autorid_fetch_config_state {
684         TALLOC_CTX *mem_ctx;
685         char *configstr;
686 };
687
688 static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
689                                         void *private_data)
690 {
691         struct idmap_autorid_fetch_config_state *state;
692
693         state = (struct idmap_autorid_fetch_config_state *)private_data;
694
695         /*
696          * strndup because we have non-nullterminated strings in the db
697          */
698         state->configstr = talloc_strndup(
699                 state->mem_ctx, (const char *)value.dptr, value.dsize);
700 }
701
702 NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
703                                     char **result)
704 {
705         TDB_DATA key;
706         NTSTATUS status;
707         struct idmap_autorid_fetch_config_state state;
708
709         if (result == NULL) {
710                 return NT_STATUS_INVALID_PARAMETER;
711         }
712
713         key = string_term_tdb_data(CONFIGKEY);
714
715         state.mem_ctx = mem_ctx;
716         state.configstr = NULL;
717
718         status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
719                                      &state);
720         if (!NT_STATUS_IS_OK(status)) {
721                 DEBUG(1, ("Error while retrieving config: %s\n",
722                           nt_errstr(status)));
723                 return status;
724         }
725
726         if (state.configstr == NULL) {
727                 DEBUG(1, ("Error while retrieving config\n"));
728                 return NT_STATUS_NO_MEMORY;
729         }
730
731         DEBUG(5, ("found CONFIG: %s\n", state.configstr));
732
733         *result = state.configstr;
734         return NT_STATUS_OK;
735 }
736
737 bool idmap_autorid_parse_configstr(const char *configstr,
738                                    struct autorid_global_config *cfg)
739 {
740         unsigned long minvalue, rangesize, maxranges;
741
742         if (sscanf(configstr,
743                    "minvalue:%lu rangesize:%lu maxranges:%lu",
744                    &minvalue, &rangesize, &maxranges) != 3) {
745                 DEBUG(1,
746                       ("Found invalid configuration data"
747                        "creating new config\n"));
748                 return false;
749         }
750
751         cfg->minvalue = minvalue;
752         cfg->rangesize = rangesize;
753         cfg->maxranges = maxranges;
754
755         return true;
756 }
757
758 NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
759                                   TALLOC_CTX *mem_ctx,
760                                   struct autorid_global_config **result)
761 {
762         struct autorid_global_config *cfg;
763         NTSTATUS status;
764         bool ok;
765         char *configstr = NULL;
766
767         if (result == NULL) {
768                 return NT_STATUS_INVALID_PARAMETER;
769         }
770
771         status = idmap_autorid_getconfigstr(db, mem_ctx, &configstr);
772         if (!NT_STATUS_IS_OK(status)) {
773                 return status;
774         }
775
776         cfg = talloc_zero(mem_ctx, struct autorid_global_config);
777         if (cfg == NULL) {
778                 return NT_STATUS_NO_MEMORY;
779         }
780
781         ok = idmap_autorid_parse_configstr(configstr, cfg);
782         if (!ok) {
783                 talloc_free(cfg);
784                 return NT_STATUS_INVALID_PARAMETER;
785         }
786
787         DEBUG(10, ("Loaded previously stored configuration "
788                    "minvalue:%d rangesize:%d\n",
789                    cfg->minvalue, cfg->rangesize));
790
791         *result = cfg;
792
793         return NT_STATUS_OK;
794 }
795
796 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
797                                   struct autorid_global_config *cfg)
798 {
799
800         struct autorid_global_config *storedconfig = NULL;
801         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
802         TDB_DATA data;
803         char *cfgstr;
804         uint32_t hwm;
805         TALLOC_CTX *frame = talloc_stackframe();
806
807         DEBUG(10, ("New configuration provided for storing is "
808                    "minvalue:%d rangesize:%d maxranges:%d\n",
809                    cfg->minvalue, cfg->rangesize, cfg->maxranges));
810
811         if (cfg->rangesize < 2000) {
812                 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
813                 goto done;
814         }
815
816         if (cfg->maxranges == 0) {
817                 DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
818                           "Must have at least one range available.\n"));
819                 goto done;
820         }
821
822         status = idmap_autorid_loadconfig(db, frame, &storedconfig);
823         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
824                 DEBUG(5, ("No configuration found. Storing initial "
825                           "configuration.\n"));
826         } else if (!NT_STATUS_IS_OK(status)) {
827                 goto done;
828         }
829
830         /* did the minimum value or rangesize change? */
831         if (storedconfig &&
832             ((storedconfig->minvalue != cfg->minvalue) ||
833              (storedconfig->rangesize != cfg->rangesize)))
834         {
835                 DEBUG(1, ("New configuration values for rangesize or "
836                           "minimum uid value conflict with previously "
837                           "used values! Not storing new config.\n"));
838                 status = NT_STATUS_INVALID_PARAMETER;
839                 goto done;
840         }
841
842         status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
843         if (!NT_STATUS_IS_OK(status)) {
844                 DEBUG(1, ("Fatal error while fetching current "
845                           "HWM value: %s\n", nt_errstr(status)));
846                 status = NT_STATUS_INTERNAL_ERROR;
847                 goto done;
848         }
849
850         /*
851          * has the highest uid value been reduced to setting that is not
852          * sufficient any more for already existing ranges?
853          */
854         if (hwm > cfg->maxranges) {
855                 DEBUG(1, ("New upper uid limit is too low to cover "
856                           "existing mappings! Not storing new config.\n"));
857                 status = NT_STATUS_INVALID_PARAMETER;
858                 goto done;
859         }
860
861         cfgstr =
862             talloc_asprintf(frame,
863                             "minvalue:%u rangesize:%u maxranges:%u",
864                             cfg->minvalue, cfg->rangesize, cfg->maxranges);
865
866         if (cfgstr == NULL) {
867                 status = NT_STATUS_NO_MEMORY;
868                 goto done;
869         }
870
871         data = string_tdb_data(cfgstr);
872
873         status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);
874
875 done:
876         TALLOC_FREE(frame);
877         return status;
878 }
879
880 NTSTATUS idmap_autorid_saveconfigstr(struct db_context *db,
881                                      const char *configstr)
882 {
883         bool ok;
884         NTSTATUS status;
885         struct autorid_global_config cfg;
886
887         ok = idmap_autorid_parse_configstr(configstr, &cfg);
888         if (!ok) {
889                 return NT_STATUS_INVALID_PARAMETER;
890         }
891
892         status = idmap_autorid_saveconfig(db, &cfg);
893         return status;
894 }