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