autorid: print debug message when a HWM key has been created
[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         if (!idmap_autorid_validate_sid(range->domsid)) {
308                 DEBUG(3, ("Invalid SID: '%s'\n", range->domsid));
309                 status = NT_STATUS_INVALID_PARAMETER;
310                 goto done;
311         }
312
313         idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
314                                    keystr);
315
316         DEBUG(10, ("reading domain range for key %s\n", keystr));
317         status = dbwrap_fetch_uint32_bystring(db, keystr, &(range->rangenum));
318         if (!NT_STATUS_IS_OK(status)) {
319                 DEBUG(1, ("Failed to read database for key '%s': %s\n",
320                           keystr, nt_errstr(status)));
321                 goto done;
322         }
323
324         status = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
325         if (!NT_STATUS_IS_OK(status)) {
326                 DEBUG(1, ("Failed to read global configuration"));
327                 goto done;
328         }
329         range->low_id = globalcfg->minvalue
330                       + range->rangenum * globalcfg->rangesize;
331
332         TALLOC_FREE(globalcfg);
333 done:
334         return status;
335 }
336
337 NTSTATUS idmap_autorid_getrange(struct db_context *db,
338                                 const char *domsid,
339                                 uint32_t domain_range_index,
340                                 uint32_t *rangenum,
341                                 uint32_t *low_id)
342 {
343         NTSTATUS status;
344         struct autorid_range_config range;
345
346         if (rangenum == NULL) {
347                 return NT_STATUS_INVALID_PARAMETER;
348         }
349
350         ZERO_STRUCT(range);
351         fstrcpy(range.domsid, domsid);
352         range.domain_range_index = domain_range_index;
353
354         status = idmap_autorid_getrange_int(db, &range);
355         if (!NT_STATUS_IS_OK(status)) {
356                 return status;
357         }
358
359         *rangenum = range.rangenum;
360
361         if (low_id != NULL) {
362                 *low_id = range.low_id;
363         }
364
365         return NT_STATUS_OK;
366 }
367
368 NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
369                                        struct autorid_range_config *range,
370                                        bool read_only)
371 {
372         NTSTATUS ret;
373
374         ret = idmap_autorid_getrange_int(db, range);
375         if (!NT_STATUS_IS_OK(ret)) {
376                 if (read_only) {
377                         return NT_STATUS_NOT_FOUND;
378                 }
379
380                 ret = idmap_autorid_acquire_range(db, range);
381         }
382
383         DEBUG(10, ("Using range #%d for domain %s "
384                    "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
385                    range->rangenum, range->domsid, range->domain_range_index,
386                    range->low_id));
387
388         return ret;
389 }
390
391 /* initialize the given HWM to 0 if it does not exist yet */
392 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
393 {
394         NTSTATUS status;
395         uint32_t hwmval;
396
397         status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
398         if (NT_STATUS_IS_OK(status)) {
399                 DEBUG(1, ("HWM (%s) already initialized in autorid database "
400                           "(value %"PRIu32").\n", hwm, hwmval));
401                 return NT_STATUS_OK;
402         }
403         if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
404                 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
405                           "database: %s\n", hwm,  nt_errstr(status)));
406                 return status;
407         }
408
409         status = dbwrap_trans_store_uint32_bystring(db, hwm, 0);
410         if (!NT_STATUS_IS_OK(status)) {
411                 DEBUG(0, ("Error initializing HWM (%s) in autorid database: "
412                           "%s\n", hwm, nt_errstr(status)));
413                 return NT_STATUS_INTERNAL_DB_ERROR;
414         }
415
416         DEBUG(1, ("Initialized HWM (%s) in autorid database.\n", hwm));
417
418         return NT_STATUS_OK;
419 }
420
421 /*
422  * Delete a domain#index <-> range mapping from the database.
423  * The mapping is specified by the sid and index.
424  * If force == true, invalid mapping records are deleted as far
425  * as possible, otherwise they are left untouched.
426  */
427
428 struct idmap_autorid_delete_range_by_sid_ctx {
429         const char *domsid;
430         uint32_t domain_range_index;
431         bool force;
432 };
433
434 static NTSTATUS idmap_autorid_delete_range_by_sid_action(struct db_context *db,
435                                                          void *private_data)
436 {
437         struct idmap_autorid_delete_range_by_sid_ctx *ctx =
438                 (struct idmap_autorid_delete_range_by_sid_ctx *)private_data;
439         const char *domsid;
440         uint32_t domain_range_index;
441         uint32_t rangenum;
442         char *keystr;
443         char *range_keystr;
444         TDB_DATA data;
445         NTSTATUS status;
446         TALLOC_CTX *frame = talloc_stackframe();
447         bool is_valid_range_mapping = true;
448         bool force;
449
450         domsid = ctx->domsid;
451         domain_range_index = ctx->domain_range_index;
452         force = ctx->force;
453
454         keystr = idmap_autorid_build_keystr_talloc(frame, domsid,
455                                                    domain_range_index);
456         if (keystr == NULL) {
457                 status = NT_STATUS_NO_MEMORY;
458                 goto done;
459         }
460
461         status = dbwrap_fetch_uint32_bystring(db, keystr, &rangenum);
462         if (!NT_STATUS_IS_OK(status)) {
463                 goto done;
464         }
465
466         range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
467         if (range_keystr == NULL) {
468                 status = NT_STATUS_NO_MEMORY;
469                 goto done;
470         }
471
472         status = dbwrap_fetch_bystring(db, frame, range_keystr, &data);
473         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
474                 DEBUG(1, ("Incomplete mapping %s -> %s: no backward mapping\n",
475                           keystr, range_keystr));
476                 is_valid_range_mapping = false;
477         } else if (!NT_STATUS_IS_OK(status)) {
478                 DEBUG(1, ("Error fetching reverse mapping for %s -> %s:  %s\n",
479                           keystr, range_keystr, nt_errstr(status)));
480                 goto done;
481         } else if (strncmp((const char *)data.dptr, keystr, strlen(keystr))
482                    != 0)
483         {
484                 DEBUG(1, ("Invalid mapping: %s -> %s -> %s\n",
485                           keystr, range_keystr, (const char *)data.dptr));
486                 is_valid_range_mapping = false;
487         }
488
489         if (!is_valid_range_mapping && !force) {
490                 DEBUG(10, ("Not deleting invalid mapping, since not in force "
491                            "mode.\n"));
492                 status = NT_STATUS_FILE_INVALID;
493                 goto done;
494         }
495
496         status = dbwrap_delete_bystring(db, keystr);
497         if (!NT_STATUS_IS_OK(status)) {
498                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
499                           keystr, nt_errstr(status)));
500                 goto done;
501         }
502
503         if (!is_valid_range_mapping) {
504                 goto done;
505         }
506
507         status = dbwrap_delete_bystring(db, range_keystr);
508         if (!NT_STATUS_IS_OK(status)) {
509                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
510                           range_keystr, nt_errstr(status)));
511                 goto done;
512         }
513
514         DEBUG(10, ("Deleted range mapping %s <--> %s\n", keystr,
515                    range_keystr));
516
517 done:
518         TALLOC_FREE(frame);
519         return status;
520 }
521
522 NTSTATUS idmap_autorid_delete_range_by_sid(struct db_context *db,
523                                            const char *domsid,
524                                            uint32_t domain_range_index,
525                                            bool force)
526 {
527         NTSTATUS status;
528         struct idmap_autorid_delete_range_by_sid_ctx ctx;
529
530         ctx.domain_range_index = domain_range_index;
531         ctx.domsid = domsid;
532         ctx.force = force;
533
534         status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_sid_action,
535                                  &ctx);
536         return status;
537 }
538
539 /*
540  * Delete a domain#index <-> range mapping from the database.
541  * The mapping is specified by the range number.
542  * If force == true, invalid mapping records are deleted as far
543  * as possible, otherwise they are left untouched.
544  */
545 struct idmap_autorid_delete_range_by_num_ctx {
546         uint32_t rangenum;
547         bool force;
548 };
549
550 static NTSTATUS idmap_autorid_delete_range_by_num_action(struct db_context *db,
551                                                            void *private_data)
552 {
553         struct idmap_autorid_delete_range_by_num_ctx *ctx =
554                 (struct idmap_autorid_delete_range_by_num_ctx *)private_data;
555         uint32_t rangenum;
556         char *keystr;
557         char *range_keystr;
558         TDB_DATA val;
559         NTSTATUS status;
560         TALLOC_CTX *frame = talloc_stackframe();
561         bool is_valid_range_mapping = true;
562         bool force;
563
564         rangenum = ctx->rangenum;
565         force = ctx->force;
566
567         range_keystr = talloc_asprintf(frame, "%"PRIu32, rangenum);
568         if (range_keystr == NULL) {
569                 status = NT_STATUS_NO_MEMORY;
570                 goto done;
571         }
572
573         ZERO_STRUCT(val);
574
575         status = dbwrap_fetch_bystring(db, frame, range_keystr, &val);
576         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
577                 DEBUG(10, ("Did not find range '%s' in database.\n",
578                            range_keystr));
579                 goto done;
580         } else if (!NT_STATUS_IS_OK(status)) {
581                 DEBUG(5, ("Error fetching rang key: %s\n", nt_errstr(status)));
582                 goto done;
583         }
584
585         if (val.dptr == NULL) {
586                 DEBUG(1, ("Invalid mapping: %s -> empty value\n",
587                           range_keystr));
588                 is_valid_range_mapping = false;
589         } else {
590                 uint32_t reverse_rangenum = 0;
591
592                 keystr = (char *)val.dptr;
593
594                 status = dbwrap_fetch_uint32_bystring(db, keystr,
595                                                       &reverse_rangenum);
596                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
597                         DEBUG(1, ("Incomplete mapping %s -> %s: "
598                                   "no backward mapping\n",
599                                   range_keystr, keystr));
600                         is_valid_range_mapping = false;
601                 } else if (!NT_STATUS_IS_OK(status)) {
602                         DEBUG(1, ("Error fetching reverse mapping for "
603                                   "%s -> %s: %s\n",
604                                   range_keystr, keystr, nt_errstr(status)));
605                         goto done;
606                 } else if (rangenum != reverse_rangenum) {
607                         is_valid_range_mapping = false;
608                 }
609         }
610
611         if (!is_valid_range_mapping && !force) {
612                 DEBUG(10, ("Not deleting invalid mapping, since not in force "
613                            "mode.\n"));
614                 status = NT_STATUS_FILE_INVALID;
615                 goto done;
616         }
617
618         status = dbwrap_delete_bystring(db, range_keystr);
619         if (!NT_STATUS_IS_OK(status)) {
620                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
621                           range_keystr, nt_errstr(status)));
622                 goto done;
623         }
624
625         if (!is_valid_range_mapping) {
626                 goto done;
627         }
628
629         status = dbwrap_delete_bystring(db, keystr);
630         if (!NT_STATUS_IS_OK(status)) {
631                 DEBUG(1, ("Deletion of '%s' failed: %s\n",
632                           keystr, nt_errstr(status)));
633                 goto done;
634         }
635
636         DEBUG(10, ("Deleted range mapping %s <--> %s\n", range_keystr,
637                    keystr));
638
639 done:
640         talloc_free(frame);
641         return status;
642 }
643
644 NTSTATUS idmap_autorid_delete_range_by_num(struct db_context *db,
645                                            uint32_t rangenum,
646                                            bool force)
647 {
648         NTSTATUS status;
649         struct idmap_autorid_delete_range_by_num_ctx ctx;
650
651         ctx.rangenum = rangenum;
652         ctx.force = force;
653
654         status = dbwrap_trans_do(db, idmap_autorid_delete_range_by_num_action,
655                                  &ctx);
656         return status;
657 }
658
659 /*
660  * open and initialize the database which stores the ranges for the domains
661  */
662 NTSTATUS idmap_autorid_db_init(const char *path,
663                                TALLOC_CTX *mem_ctx,
664                                struct db_context **db)
665 {
666         NTSTATUS status;
667
668         if (*db != NULL) {
669                 /* its already open */
670                 return NT_STATUS_OK;
671         }
672
673         /* Open idmap repository */
674         *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
675                       DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
676
677         if (*db == NULL) {
678                 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
679                 return NT_STATUS_UNSUCCESSFUL;
680         }
681
682         /* Initialize high water mark for the currently used range to 0 */
683
684         status = idmap_autorid_init_hwm(*db, HWM);
685         NT_STATUS_NOT_OK_RETURN(status);
686
687         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
688         NT_STATUS_NOT_OK_RETURN(status);
689
690         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
691
692         return status;
693 }
694
695 struct idmap_autorid_fetch_config_state {
696         TALLOC_CTX *mem_ctx;
697         char *configstr;
698 };
699
700 static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
701                                         void *private_data)
702 {
703         struct idmap_autorid_fetch_config_state *state;
704
705         state = (struct idmap_autorid_fetch_config_state *)private_data;
706
707         /*
708          * strndup because we have non-nullterminated strings in the db
709          */
710         state->configstr = talloc_strndup(
711                 state->mem_ctx, (const char *)value.dptr, value.dsize);
712 }
713
714 NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
715                                     char **result)
716 {
717         TDB_DATA key;
718         NTSTATUS status;
719         struct idmap_autorid_fetch_config_state state;
720
721         if (result == NULL) {
722                 return NT_STATUS_INVALID_PARAMETER;
723         }
724
725         key = string_term_tdb_data(CONFIGKEY);
726
727         state.mem_ctx = mem_ctx;
728         state.configstr = NULL;
729
730         status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
731                                      &state);
732         if (!NT_STATUS_IS_OK(status)) {
733                 DEBUG(1, ("Error while retrieving config: %s\n",
734                           nt_errstr(status)));
735                 return status;
736         }
737
738         if (state.configstr == NULL) {
739                 DEBUG(1, ("Error while retrieving config\n"));
740                 return NT_STATUS_NO_MEMORY;
741         }
742
743         DEBUG(5, ("found CONFIG: %s\n", state.configstr));
744
745         *result = state.configstr;
746         return NT_STATUS_OK;
747 }
748
749 bool idmap_autorid_parse_configstr(const char *configstr,
750                                    struct autorid_global_config *cfg)
751 {
752         unsigned long minvalue, rangesize, maxranges;
753
754         if (sscanf(configstr,
755                    "minvalue:%lu rangesize:%lu maxranges:%lu",
756                    &minvalue, &rangesize, &maxranges) != 3) {
757                 DEBUG(1,
758                       ("Found invalid configuration data. "
759                        "Creating new config\n"));
760                 return false;
761         }
762
763         cfg->minvalue = minvalue;
764         cfg->rangesize = rangesize;
765         cfg->maxranges = maxranges;
766
767         return true;
768 }
769
770 NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
771                                   TALLOC_CTX *mem_ctx,
772                                   struct autorid_global_config **result)
773 {
774         struct autorid_global_config *cfg;
775         NTSTATUS status;
776         bool ok;
777         char *configstr = NULL;
778
779         if (result == NULL) {
780                 return NT_STATUS_INVALID_PARAMETER;
781         }
782
783         status = idmap_autorid_getconfigstr(db, mem_ctx, &configstr);
784         if (!NT_STATUS_IS_OK(status)) {
785                 return status;
786         }
787
788         cfg = talloc_zero(mem_ctx, struct autorid_global_config);
789         if (cfg == NULL) {
790                 return NT_STATUS_NO_MEMORY;
791         }
792
793         ok = idmap_autorid_parse_configstr(configstr, cfg);
794         if (!ok) {
795                 talloc_free(cfg);
796                 return NT_STATUS_INVALID_PARAMETER;
797         }
798
799         DEBUG(10, ("Loaded previously stored configuration "
800                    "minvalue:%d rangesize:%d\n",
801                    cfg->minvalue, cfg->rangesize));
802
803         *result = cfg;
804
805         return NT_STATUS_OK;
806 }
807
808 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
809                                   struct autorid_global_config *cfg)
810 {
811
812         struct autorid_global_config *storedconfig = NULL;
813         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
814         TDB_DATA data;
815         char *cfgstr;
816         uint32_t hwm;
817         TALLOC_CTX *frame = talloc_stackframe();
818
819         DEBUG(10, ("New configuration provided for storing is "
820                    "minvalue:%d rangesize:%d maxranges:%d\n",
821                    cfg->minvalue, cfg->rangesize, cfg->maxranges));
822
823         if (cfg->rangesize < 2000) {
824                 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
825                 goto done;
826         }
827
828         if (cfg->maxranges == 0) {
829                 DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
830                           "Must have at least one range available.\n"));
831                 goto done;
832         }
833
834         status = idmap_autorid_loadconfig(db, frame, &storedconfig);
835         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
836                 DEBUG(5, ("No configuration found. Storing initial "
837                           "configuration.\n"));
838         } else if (!NT_STATUS_IS_OK(status)) {
839                 goto done;
840         }
841
842         /* did the minimum value or rangesize change? */
843         if (storedconfig &&
844             ((storedconfig->minvalue != cfg->minvalue) ||
845              (storedconfig->rangesize != cfg->rangesize)))
846         {
847                 DEBUG(1, ("New configuration values for rangesize or "
848                           "minimum uid value conflict with previously "
849                           "used values! Not storing new config.\n"));
850                 status = NT_STATUS_INVALID_PARAMETER;
851                 goto done;
852         }
853
854         status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
855         if (!NT_STATUS_IS_OK(status)) {
856                 DEBUG(1, ("Fatal error while fetching current "
857                           "HWM value: %s\n", nt_errstr(status)));
858                 status = NT_STATUS_INTERNAL_ERROR;
859                 goto done;
860         }
861
862         /*
863          * has the highest uid value been reduced to setting that is not
864          * sufficient any more for already existing ranges?
865          */
866         if (hwm > cfg->maxranges) {
867                 DEBUG(1, ("New upper uid limit is too low to cover "
868                           "existing mappings! Not storing new config.\n"));
869                 status = NT_STATUS_INVALID_PARAMETER;
870                 goto done;
871         }
872
873         cfgstr =
874             talloc_asprintf(frame,
875                             "minvalue:%u rangesize:%u maxranges:%u",
876                             cfg->minvalue, cfg->rangesize, cfg->maxranges);
877
878         if (cfgstr == NULL) {
879                 status = NT_STATUS_NO_MEMORY;
880                 goto done;
881         }
882
883         data = string_tdb_data(cfgstr);
884
885         status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);
886
887 done:
888         TALLOC_FREE(frame);
889         return status;
890 }
891
892 NTSTATUS idmap_autorid_saveconfigstr(struct db_context *db,
893                                      const char *configstr)
894 {
895         bool ok;
896         NTSTATUS status;
897         struct autorid_global_config cfg;
898
899         ok = idmap_autorid_parse_configstr(configstr, &cfg);
900         if (!ok) {
901                 return NT_STATUS_INVALID_PARAMETER;
902         }
903
904         status = idmap_autorid_saveconfig(db, &cfg);
905         return status;
906 }
907
908
909 /*
910  * iteration: Work on all range mappings for a given domain
911  */
912
913 struct domain_range_visitor_ctx {
914         const char *domsid;
915         NTSTATUS (*fn)(struct db_context *db,
916                        const char *domsid,
917                        uint32_t index,
918                        uint32_t rangenum,
919                        void *private_data);
920         void *private_data;
921         int count; /* number of records worked on */
922 };
923
924 static int idmap_autorid_visit_domain_range(struct db_record *rec,
925                                             void *private_data)
926 {
927         struct domain_range_visitor_ctx *vi;
928         char *domsid;
929         char *sep;
930         uint32_t range_index = 0;
931         uint32_t rangenum = 0;
932         TDB_DATA key, value;
933         NTSTATUS status;
934         int ret = 0;
935         struct db_context *db;
936
937         vi = talloc_get_type_abort(private_data,
938                                    struct domain_range_visitor_ctx);
939
940         key = dbwrap_record_get_key(rec);
941
942         /*
943          * split string "<sid>[#<index>]" into sid string and index number
944          */
945
946         domsid = (char *)key.dptr;
947
948         DEBUG(10, ("idmap_autorid_visit_domain_range: visiting key '%s'\n",
949                    domsid));
950
951         sep = strrchr(domsid, '#');
952         if (sep != NULL) {
953                 char *index_str;
954                 *sep = '\0';
955                 index_str = sep+1;
956                 if (sscanf(index_str, "%"SCNu32, &range_index) != 1) {
957                         DEBUG(10, ("Found separator '#' but '%s' is not a "
958                                    "valid range index. Skipping record\n",
959                                    index_str));
960                         goto done;
961                 }
962         }
963
964         if (!idmap_autorid_validate_sid(domsid)) {
965                 DEBUG(10, ("String '%s' is not a valid sid. "
966                            "Skipping record.\n", domsid));
967                 goto done;
968         }
969
970         if ((vi->domsid != NULL) && (strcmp(domsid, vi->domsid) != 0)) {
971                 DEBUG(10, ("key sid '%s' does not match requested sid '%s'.\n",
972                            domsid, vi->domsid));
973                 goto done;
974         }
975
976         value = dbwrap_record_get_value(rec);
977
978         if (value.dsize != sizeof(uint32_t)) {
979                 /* it might be a mapping of a well known sid */
980                 DEBUG(10, ("value size %u != sizeof(uint32_t) for sid '%s', "
981                            "skipping.\n", (unsigned)value.dsize, vi->domsid));
982                 goto done;
983         }
984
985         rangenum = IVAL(value.dptr, 0);
986
987         db = dbwrap_record_get_db(rec);
988
989         status = vi->fn(db, domsid, range_index, rangenum, vi->private_data);
990         if (!NT_STATUS_IS_OK(status)) {
991                 ret = -1;
992                 goto done;
993         }
994
995         vi->count++;
996         ret = 0;
997
998 done:
999         return ret;
1000 }
1001
1002 static NTSTATUS idmap_autorid_iterate_domain_ranges_int(struct db_context *db,
1003                                 const char *domsid,
1004                                 NTSTATUS (*fn)(struct db_context *db,
1005                                                const char *domsid,
1006                                                uint32_t index,
1007                                                uint32_t rangnum,
1008                                                void *private_data),
1009                                 void *private_data,
1010                                 int *count,
1011                                 NTSTATUS (*traverse)(struct db_context *db,
1012                                           int (*f)(struct db_record *, void *),
1013                                           void *private_data,
1014                                           int *count))
1015 {
1016         NTSTATUS status;
1017         struct domain_range_visitor_ctx *vi;
1018         TALLOC_CTX *frame = talloc_stackframe();
1019
1020         if (domsid == NULL) {
1021                 DEBUG(10, ("No sid provided, operating on all ranges\n"));
1022         }
1023
1024         if (fn == NULL) {
1025                 DEBUG(1, ("Error: missing visitor callback\n"));
1026                 status = NT_STATUS_INVALID_PARAMETER;
1027                 goto done;
1028         }
1029
1030         vi = talloc_zero(frame, struct domain_range_visitor_ctx);
1031         if (vi == NULL) {
1032                 status = NT_STATUS_NO_MEMORY;
1033                 goto done;
1034         }
1035
1036         vi->domsid = domsid;
1037         vi->fn = fn;
1038         vi->private_data = private_data;
1039
1040         status = traverse(db, idmap_autorid_visit_domain_range, vi, NULL);
1041         if (!NT_STATUS_IS_OK(status)) {
1042                 goto done;
1043         }
1044
1045         if (count != NULL) {
1046                 *count = vi->count;
1047         }
1048
1049 done:
1050         talloc_free(frame);
1051         return status;
1052 }
1053
1054 NTSTATUS idmap_autorid_iterate_domain_ranges(struct db_context *db,
1055                                         const char *domsid,
1056                                         NTSTATUS (*fn)(struct db_context *db,
1057                                                        const char *domsid,
1058                                                        uint32_t index,
1059                                                        uint32_t rangenum,
1060                                                        void *private_data),
1061                                         void *private_data,
1062                                         int *count)
1063 {
1064         NTSTATUS status;
1065
1066         status = idmap_autorid_iterate_domain_ranges_int(db,
1067                                                          domsid,
1068                                                          fn,
1069                                                          private_data,
1070                                                          count,
1071                                                          dbwrap_traverse);
1072
1073         return status;
1074 }
1075
1076
1077 NTSTATUS idmap_autorid_iterate_domain_ranges_read(struct db_context *db,
1078                                         const char *domsid,
1079                                         NTSTATUS (*fn)(struct db_context *db,
1080                                                        const char *domsid,
1081                                                        uint32_t index,
1082                                                        uint32_t rangenum,
1083                                                        void *count),
1084                                         void *private_data,
1085                                         int *count)
1086 {
1087         NTSTATUS status;
1088
1089         status = idmap_autorid_iterate_domain_ranges_int(db,
1090                                                          domsid,
1091                                                          fn,
1092                                                          private_data,
1093                                                          count,
1094                                                          dbwrap_traverse_read);
1095
1096         return status;
1097 }
1098
1099
1100 /*
1101  * Delete all ranges configured for a given domain
1102  */
1103
1104 struct delete_domain_ranges_visitor_ctx {
1105         bool force;
1106 };
1107
1108 static NTSTATUS idmap_autorid_delete_domain_ranges_visitor(
1109                                                 struct db_context *db,
1110                                                 const char *domsid,
1111                                                 uint32_t domain_range_index,
1112                                                 uint32_t rangenum,
1113                                                 void *private_data)
1114 {
1115         struct delete_domain_ranges_visitor_ctx *ctx;
1116         NTSTATUS status;
1117
1118         ctx = (struct delete_domain_ranges_visitor_ctx *)private_data;
1119
1120         status = idmap_autorid_delete_range_by_sid(
1121                                 db, domsid, domain_range_index, ctx->force);
1122         return status;
1123 }
1124
1125 struct idmap_autorid_delete_domain_ranges_ctx {
1126         const char *domsid;
1127         bool force;
1128         int count; /* output: count records operated on */
1129 };
1130
1131 static NTSTATUS idmap_autorid_delete_domain_ranges_action(struct db_context *db,
1132                                                           void *private_data)
1133 {
1134         struct idmap_autorid_delete_domain_ranges_ctx *ctx;
1135         struct delete_domain_ranges_visitor_ctx visitor_ctx;
1136         int count;
1137         NTSTATUS status;
1138
1139         ctx = (struct idmap_autorid_delete_domain_ranges_ctx *)private_data;
1140
1141         ZERO_STRUCT(visitor_ctx);
1142         visitor_ctx.force = ctx->force;
1143
1144         status = idmap_autorid_iterate_domain_ranges(db,
1145                                 ctx->domsid,
1146                                 idmap_autorid_delete_domain_ranges_visitor,
1147                                 &visitor_ctx,
1148                                 &count);
1149         if (!NT_STATUS_IS_OK(status)) {
1150                 return status;
1151         }
1152
1153         ctx->count = count;
1154
1155         return NT_STATUS_OK;
1156 }
1157
1158 NTSTATUS idmap_autorid_delete_domain_ranges(struct db_context *db,
1159                                             const char *domsid,
1160                                             bool force,
1161                                             int *count)
1162 {
1163         NTSTATUS status;
1164         struct idmap_autorid_delete_domain_ranges_ctx ctx;
1165
1166         ZERO_STRUCT(ctx);
1167         ctx.domsid = domsid;
1168         ctx.force = force;
1169
1170         status = dbwrap_trans_do(db, idmap_autorid_delete_domain_ranges_action,
1171                                  &ctx);
1172         if (!NT_STATUS_IS_OK(status)) {
1173                 return status;
1174         }
1175
1176         *count = ctx.count;
1177
1178         return NT_STATUS_OK;
1179 }