idmap_autorid: Don't use db as a temporary talloc context.
[mat/samba.git] / source3 / winbindd / idmap_autorid_tdb.c
1 /*
2  *  idmap_autorid_tdb: This file contains common code used by
3  *  idmap_autorid and net idmap autorid utilities. The common
4  *  code provides functions for performing various operations
5  *  on autorid.tdb
6  *
7  *  Copyright (C) Christian Ambach, 2010-2012
8  *  Copyright (C) Atul Kulkarni, 2013
9  *  Copyright (C) Michael Adam, 2012-2013
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 3 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
23  *
24  */
25
26 #include "idmap_autorid_tdb.h"
27 #include "../libcli/security/dom_sid.h"
28
29 /**
30  * Build the database keystring for getting a range
31  * belonging to a domain sid and a range index.
32  */
33 static void idmap_autorid_build_keystr(const char *domsid,
34                                        uint32_t domain_range_index,
35                                        fstring keystr)
36 {
37         if (domain_range_index > 0) {
38                 fstr_sprintf(keystr, "%s#%"PRIu32,
39                              domsid, domain_range_index);
40         } else {
41                 fstrcpy(keystr, domsid);
42         }
43 }
44
45 static bool idmap_autorid_validate_sid(const char *sid)
46 {
47         struct dom_sid ignore;
48         if (sid == NULL) {
49                 return false;
50         }
51
52         if (strcmp(sid, ALLOC_RANGE) == 0) {
53                 return true;
54         }
55
56         return dom_sid_parse(sid, &ignore);
57 }
58
59 struct idmap_autorid_addrange_ctx {
60         struct autorid_range_config *range;
61         bool acquire;
62 };
63
64 static NTSTATUS idmap_autorid_addrange_action(struct db_context *db,
65                                               void *private_data)
66 {
67         struct idmap_autorid_addrange_ctx *ctx;
68         uint32_t requested_rangenum, stored_rangenum;
69         struct autorid_range_config *range;
70         bool acquire;
71         NTSTATUS ret;
72         uint32_t hwm;
73         char *numstr;
74         struct autorid_global_config *globalcfg;
75         fstring keystr;
76         uint32_t increment;
77
78         ctx = (struct idmap_autorid_addrange_ctx *)private_data;
79         range = ctx->range;
80         acquire = ctx->acquire;
81         requested_rangenum = range->rangenum;
82
83         if (db == NULL) {
84                 DEBUG(3, ("Invalid database argument: NULL"));
85                 return NT_STATUS_INVALID_PARAMETER;
86         }
87
88         if (range == NULL) {
89                 DEBUG(3, ("Invalid range argument: NULL"));
90                 return NT_STATUS_INVALID_PARAMETER;
91         }
92
93         DEBUG(10, ("Adding new range for domain %s "
94                    "(domain_range_index=%"PRIu32")\n",
95                    range->domsid, range->domain_range_index));
96
97         if (!idmap_autorid_validate_sid(range->domsid)) {
98                 DEBUG(3, ("Invalid SID: %s\n", range->domsid));
99                 return NT_STATUS_INVALID_PARAMETER;
100         }
101
102         idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
103                                    keystr);
104
105         ret = dbwrap_fetch_uint32_bystring(db, keystr, &stored_rangenum);
106
107         if (NT_STATUS_IS_OK(ret)) {
108                 /* entry is already present*/
109                 if (acquire) {
110                         DEBUG(10, ("domain range already allocated - "
111                                    "Not adding!\n"));
112                         return NT_STATUS_OK;
113                 }
114
115                 if (stored_rangenum != requested_rangenum) {
116                         DEBUG(1, ("Error: requested rangenumber (%u) differs "
117                                   "from stored one (%u).\n",
118                                   requested_rangenum, stored_rangenum));
119                         return NT_STATUS_UNSUCCESSFUL;
120                 }
121
122                 DEBUG(10, ("Note: stored range agrees with requested "
123                            "one - ok\n"));
124                 return NT_STATUS_OK;
125         }
126
127         /* fetch the current HWM */
128         ret = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
129         if (!NT_STATUS_IS_OK(ret)) {
130                 DEBUG(1, ("Fatal error while fetching current "
131                           "HWM value: %s\n", nt_errstr(ret)));
132                 ret = NT_STATUS_INTERNAL_ERROR;
133                 goto error;
134         }
135
136         ret = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
137         if (!NT_STATUS_IS_OK(ret)) {
138                 DEBUG(1, ("Fatal error while fetching configuration: %s\n",
139                           nt_errstr(ret)));
140                 goto error;
141         }
142
143         if (acquire) {
144                 /*
145                  * automatically acquire the next range
146                  */
147                 requested_rangenum = hwm;
148         } else {
149                 /*
150                  * set a specified range
151                  */
152
153                 if (requested_rangenum < hwm) {
154                         DEBUG(3, ("Invalid range %u requested: Range may not "
155                                   "be smaller than %u (current HWM)\n",
156                                   requested_rangenum, hwm));
157                         ret = NT_STATUS_INVALID_PARAMETER;
158                         goto error;
159                 }
160         }
161
162         if (requested_rangenum >= globalcfg->maxranges) {
163                 DEBUG(1, ("Not enough ranges available: New range %u must be "
164                           "smaller than configured maximum number of ranges "
165                           "(%u).\n",
166                           requested_rangenum, globalcfg->maxranges));
167                 ret = NT_STATUS_NO_MEMORY;
168                 goto error;
169         }
170         TALLOC_FREE(globalcfg);
171
172         /* HWM always contains current max range + 1 */
173         increment = requested_rangenum + 1 - hwm;
174
175         /* increase the HWM */
176         ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &hwm, increment);
177         if (!NT_STATUS_IS_OK(ret)) {
178                 DEBUG(1, ("Fatal error while incrementing the HWM value "
179                           "in the database: %s\n", nt_errstr(ret)));
180                 goto error;
181         }
182
183         /* store away the new mapping in both directions */
184         ret = dbwrap_store_uint32_bystring(db, keystr, requested_rangenum);
185         if (!NT_STATUS_IS_OK(ret)) {
186                 DEBUG(1, ("Fatal error while storing new "
187                           "domain->range assignment: %s\n", nt_errstr(ret)));
188                 goto error;
189         }
190
191         numstr = talloc_asprintf(talloc_tos(), "%u", requested_rangenum);
192         if (!numstr) {
193                 ret = NT_STATUS_NO_MEMORY;
194                 goto error;
195         }
196
197         ret = dbwrap_store_bystring(db, numstr,
198                         string_term_tdb_data(keystr), TDB_INSERT);
199
200         talloc_free(numstr);
201         if (!NT_STATUS_IS_OK(ret)) {
202                 DEBUG(1, ("Fatal error while storing new "
203                           "domain->range assignment: %s\n", nt_errstr(ret)));
204                 goto error;
205         }
206         DEBUG(5, ("Acquired new range #%d for domain %s "
207                   "(domain_range_index=%"PRIu32")\n", requested_rangenum, keystr,
208                   range->domain_range_index));
209
210         range->rangenum = requested_rangenum;
211
212         range->low_id = globalcfg->minvalue
213                       + range->rangenum * globalcfg->rangesize;
214
215         return NT_STATUS_OK;
216
217 error:
218         return ret;
219 }
220
221 static NTSTATUS idmap_autorid_addrange(struct db_context *db,
222                                        struct autorid_range_config *range,
223                                        bool acquire)
224 {
225         NTSTATUS status;
226         struct idmap_autorid_addrange_ctx ctx;
227
228         ctx.acquire = acquire;
229         ctx.range = range;
230
231         status = dbwrap_trans_do(db, idmap_autorid_addrange_action, &ctx);
232         return status;
233 }
234
235 NTSTATUS idmap_autorid_setrange(struct db_context *db,
236                                 const char *domsid,
237                                 uint32_t domain_range_index,
238                                 uint32_t rangenum)
239 {
240         NTSTATUS status;
241         struct autorid_range_config range;
242
243         ZERO_STRUCT(range);
244         fstrcpy(range.domsid, domsid);
245         range.domain_range_index = domain_range_index;
246         range.rangenum = rangenum;
247
248         status = idmap_autorid_addrange(db, &range, false);
249         return status;
250 }
251
252 static NTSTATUS idmap_autorid_acquire_range(struct db_context *db,
253                                             struct autorid_range_config *range)
254 {
255         return idmap_autorid_addrange(db, range, true);
256 }
257
258 static NTSTATUS idmap_autorid_getrange_int(struct db_context *db,
259                                            struct autorid_range_config *range)
260 {
261         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
262         struct autorid_global_config *globalcfg = NULL;
263         fstring keystr;
264
265         if (db == NULL || range == NULL) {
266                 DEBUG(3, ("Invalid arguments received\n"));
267                 goto done;
268         }
269
270         idmap_autorid_build_keystr(range->domsid, range->domain_range_index,
271                                    keystr);
272
273         DEBUG(10, ("reading domain range for key %s\n", keystr));
274         status = dbwrap_fetch_uint32_bystring(db, keystr, &(range->rangenum));
275         if (!NT_STATUS_IS_OK(status)) {
276                 DEBUG(1, ("Failed to read database for key '%s': %s\n",
277                           keystr, nt_errstr(status)));
278                 goto done;
279         }
280
281         status = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
282         if (!NT_STATUS_IS_OK(status)) {
283                 DEBUG(1, ("Failed to read global configuration"));
284                 goto done;
285         }
286         range->low_id = globalcfg->minvalue
287                       + range->rangenum * globalcfg->rangesize;
288
289         TALLOC_FREE(globalcfg);
290 done:
291         return status;
292 }
293
294 NTSTATUS idmap_autorid_getrange(struct db_context *db,
295                                 const char *domsid,
296                                 uint32_t domain_range_index,
297                                 uint32_t *rangenum,
298                                 uint32_t *low_id)
299 {
300         NTSTATUS status;
301         struct autorid_range_config range;
302
303         if (rangenum == NULL) {
304                 return NT_STATUS_INVALID_PARAMETER;
305         }
306
307         ZERO_STRUCT(range);
308         fstrcpy(range.domsid, domsid);
309         range.domain_range_index = domain_range_index;
310
311         status = idmap_autorid_getrange_int(db, &range);
312         if (!NT_STATUS_IS_OK(status)) {
313                 return status;
314         }
315
316         *rangenum = range.rangenum;
317
318         if (low_id != NULL) {
319                 *low_id = range.low_id;
320         }
321
322         return NT_STATUS_OK;
323 }
324
325 NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
326                                        struct autorid_range_config *range,
327                                        bool read_only)
328 {
329         NTSTATUS ret;
330
331         ret = idmap_autorid_getrange_int(db, range);
332         if (!NT_STATUS_IS_OK(ret)) {
333                 if (read_only) {
334                         return NT_STATUS_NOT_FOUND;
335                 }
336
337                 ret = idmap_autorid_acquire_range(db, range);
338         }
339
340         DEBUG(10, ("Using range #%d for domain %s "
341                    "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
342                    range->rangenum, range->domsid, range->domain_range_index,
343                    range->low_id));
344
345         return ret;
346 }
347
348 /* initialize the given HWM to 0 if it does not exist yet */
349 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
350 {
351         NTSTATUS status;
352         uint32_t hwmval;
353
354         status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
355         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND))  {
356                 status = dbwrap_trans_store_int32_bystring(db, hwm, 0);
357                 if (!NT_STATUS_IS_OK(status)) {
358                         DEBUG(0,
359                               ("Unable to initialise HWM (%s) in autorid "
360                                "database: %s\n", hwm, nt_errstr(status)));
361                         return NT_STATUS_INTERNAL_DB_ERROR;
362                 }
363         } else if (!NT_STATUS_IS_OK(status)) {
364                 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
365                           "database: %s\n", hwm,  nt_errstr(status)));
366                 return status;
367         }
368
369         return NT_STATUS_OK;
370 }
371
372 /*
373  * open and initialize the database which stores the ranges for the domains
374  */
375 NTSTATUS idmap_autorid_db_init(const char *path,
376                                TALLOC_CTX *mem_ctx,
377                                struct db_context **db)
378 {
379         NTSTATUS status;
380
381         if (*db != NULL) {
382                 /* its already open */
383                 return NT_STATUS_OK;
384         }
385
386         /* Open idmap repository */
387         *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
388                       DBWRAP_LOCK_ORDER_1);
389
390         if (*db == NULL) {
391                 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
392                 return NT_STATUS_UNSUCCESSFUL;
393         }
394
395         /* Initialize high water mark for the currently used range to 0 */
396
397         status = idmap_autorid_init_hwm(*db, HWM);
398         NT_STATUS_NOT_OK_RETURN(status);
399
400         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
401         NT_STATUS_NOT_OK_RETURN(status);
402
403         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
404
405         return status;
406 }
407
408 struct idmap_autorid_fetch_config_state {
409         TALLOC_CTX *mem_ctx;
410         char *configstr;
411 };
412
413 static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
414                                         void *private_data)
415 {
416         struct idmap_autorid_fetch_config_state *state;
417
418         state = (struct idmap_autorid_fetch_config_state *)private_data;
419
420         /*
421          * strndup because we have non-nullterminated strings in the db
422          */
423         state->configstr = talloc_strndup(
424                 state->mem_ctx, (const char *)value.dptr, value.dsize);
425 }
426
427 NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
428                                     char **result)
429 {
430         TDB_DATA key;
431         NTSTATUS status;
432         struct idmap_autorid_fetch_config_state state;
433
434         if (result == NULL) {
435                 return NT_STATUS_INVALID_PARAMETER;
436         }
437
438         key = string_term_tdb_data(CONFIGKEY);
439
440         state.mem_ctx = mem_ctx;
441         state.configstr = NULL;
442
443         status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
444                                      &state);
445         if (!NT_STATUS_IS_OK(status)) {
446                 DEBUG(1, ("Error while retrieving config: %s\n",
447                           nt_errstr(status)));
448                 return status;
449         }
450
451         if (state.configstr == NULL) {
452                 DEBUG(1, ("Error while retrieving config\n"));
453                 return NT_STATUS_NO_MEMORY;
454         }
455
456         DEBUG(5, ("found CONFIG: %s\n", state.configstr));
457
458         *result = state.configstr;
459         return NT_STATUS_OK;
460 }
461
462 bool idmap_autorid_parse_configstr(const char *configstr,
463                                    struct autorid_global_config *cfg)
464 {
465         unsigned long minvalue, rangesize, maxranges;
466
467         if (sscanf(configstr,
468                    "minvalue:%lu rangesize:%lu maxranges:%lu",
469                    &minvalue, &rangesize, &maxranges) != 3) {
470                 DEBUG(1,
471                       ("Found invalid configuration data"
472                        "creating new config\n"));
473                 return false;
474         }
475
476         cfg->minvalue = minvalue;
477         cfg->rangesize = rangesize;
478         cfg->maxranges = maxranges;
479
480         return true;
481 }
482
483 NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
484                                   TALLOC_CTX *mem_ctx,
485                                   struct autorid_global_config **result)
486 {
487         struct autorid_global_config *cfg;
488         NTSTATUS status;
489         bool ok;
490         char *configstr = NULL;
491
492         if (result == NULL) {
493                 return NT_STATUS_INVALID_PARAMETER;
494         }
495
496         status = idmap_autorid_getconfigstr(db, mem_ctx, &configstr);
497         if (!NT_STATUS_IS_OK(status)) {
498                 return status;
499         }
500
501         cfg = talloc_zero(mem_ctx, struct autorid_global_config);
502         if (cfg == NULL) {
503                 return NT_STATUS_NO_MEMORY;
504         }
505
506         ok = idmap_autorid_parse_configstr(configstr, cfg);
507         if (!ok) {
508                 talloc_free(cfg);
509                 return NT_STATUS_INVALID_PARAMETER;
510         }
511
512         DEBUG(10, ("Loaded previously stored configuration "
513                    "minvalue:%d rangesize:%d\n",
514                    cfg->minvalue, cfg->rangesize));
515
516         *result = cfg;
517
518         return NT_STATUS_OK;
519 }
520
521 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
522                                   struct autorid_global_config *cfg)
523 {
524
525         struct autorid_global_config *storedconfig = NULL;
526         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
527         TDB_DATA data;
528         char *cfgstr;
529         uint32_t hwm;
530         TALLOC_CTX *frame = talloc_stackframe();
531
532         DEBUG(10, ("New configuration provided for storing is "
533                    "minvalue:%d rangesize:%d maxranges:%d\n",
534                    cfg->minvalue, cfg->rangesize, cfg->maxranges));
535
536         if (cfg->rangesize < 2000) {
537                 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
538                 goto done;
539         }
540
541         if (cfg->maxranges == 0) {
542                 DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
543                           "Must have at least one range available.\n"));
544                 goto done;
545         }
546
547         status = idmap_autorid_loadconfig(db, frame, &storedconfig);
548         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
549                 DEBUG(5, ("No configuration found. Storing initial "
550                           "configuration.\n"));
551         } else if (!NT_STATUS_IS_OK(status)) {
552                 goto done;
553         }
554
555         /* did the minimum value or rangesize change? */
556         if (storedconfig &&
557             ((storedconfig->minvalue != cfg->minvalue) ||
558              (storedconfig->rangesize != cfg->rangesize)))
559         {
560                 DEBUG(1, ("New configuration values for rangesize or "
561                           "minimum uid value conflict with previously "
562                           "used values! Not storing new config.\n"));
563                 status = NT_STATUS_INVALID_PARAMETER;
564                 goto done;
565         }
566
567         status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
568         if (!NT_STATUS_IS_OK(status)) {
569                 DEBUG(1, ("Fatal error while fetching current "
570                           "HWM value: %s\n", nt_errstr(status)));
571                 status = NT_STATUS_INTERNAL_ERROR;
572                 goto done;
573         }
574
575         /*
576          * has the highest uid value been reduced to setting that is not
577          * sufficient any more for already existing ranges?
578          */
579         if (hwm > cfg->maxranges) {
580                 DEBUG(1, ("New upper uid limit is too low to cover "
581                           "existing mappings! Not storing new config.\n"));
582                 status = NT_STATUS_INVALID_PARAMETER;
583                 goto done;
584         }
585
586         cfgstr =
587             talloc_asprintf(frame,
588                             "minvalue:%u rangesize:%u maxranges:%u",
589                             cfg->minvalue, cfg->rangesize, cfg->maxranges);
590
591         if (cfgstr == NULL) {
592                 status = NT_STATUS_NO_MEMORY;
593                 goto done;
594         }
595
596         data = string_tdb_data(cfgstr);
597
598         status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);
599
600 done:
601         TALLOC_FREE(frame);
602         return status;
603 }