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