idmap_autorid: remove fstring keystr from autorid_range_config
[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
28 static NTSTATUS idmap_autorid_get_domainrange_action(struct db_context *db,
29                                               void *private_data)
30 {
31         NTSTATUS ret;
32         uint32_t rangenum, hwm;
33         char *numstr;
34         struct autorid_range_config *range;
35         struct autorid_global_config *globalcfg;
36         fstring keystr;
37
38         range = (struct autorid_range_config *)private_data;
39
40         if (range->domain_range_index > 0) {
41                 snprintf(keystr, FSTRING_LEN, "%s#%"PRIu32,
42                          range->domsid, range->domain_range_index);
43         } else {
44                 fstrcpy(keystr, range->domsid);
45         }
46
47         ret = dbwrap_fetch_uint32_bystring(db, keystr,
48                                            &(range->rangenum));
49
50         if (NT_STATUS_IS_OK(ret)) {
51                 /* entry is already present*/
52                 return ret;
53         }
54
55         DEBUG(10, ("Acquiring new range for domain %s "
56                    "(domain_range_index=%"PRIu32")\n",
57                    range->domsid, range->domain_range_index));
58
59         /* fetch the current HWM */
60         ret = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
61         if (!NT_STATUS_IS_OK(ret)) {
62                 DEBUG(1, ("Fatal error while fetching current "
63                           "HWM value: %s\n", nt_errstr(ret)));
64                 ret = NT_STATUS_INTERNAL_ERROR;
65                 goto error;
66         }
67
68         ret = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
69         if (!NT_STATUS_IS_OK(ret)) {
70                 return ret;
71         }
72
73         /* do we have a range left? */
74         if (hwm >= globalcfg->maxranges) {
75                 DEBUG(1, ("No more domain ranges available!\n"));
76                 talloc_free(globalcfg);
77                 ret = NT_STATUS_NO_MEMORY;
78                 goto error;
79         }
80         TALLOC_FREE(globalcfg);
81
82         /* increase the HWM */
83         ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &rangenum, 1);
84         if (!NT_STATUS_IS_OK(ret)) {
85                 DEBUG(1, ("Fatal error while fetching a new "
86                           "domain range value!\n"));
87                 goto error;
88         }
89
90         /* store away the new mapping in both directions */
91         ret = dbwrap_store_uint32_bystring(db, keystr, rangenum);
92         if (!NT_STATUS_IS_OK(ret)) {
93                 DEBUG(1, ("Fatal error while storing new "
94                           "domain->range assignment!\n"));
95                 goto error;
96         }
97
98         numstr = talloc_asprintf(db, "%u", rangenum);
99         if (!numstr) {
100                 ret = NT_STATUS_NO_MEMORY;
101                 goto error;
102         }
103
104         ret = dbwrap_store_bystring(db, numstr,
105                         string_term_tdb_data(keystr), TDB_INSERT);
106
107         talloc_free(numstr);
108         if (!NT_STATUS_IS_OK(ret)) {
109                 DEBUG(1, ("Fatal error while storing "
110                           "new domain->range assignment!\n"));
111                 goto error;
112         }
113         DEBUG(5, ("Acquired new range #%d for domain %s "
114                   "(domain_range_index=%"PRIu32")\n", rangenum, keystr,
115                   range->domain_range_index));
116
117         range->rangenum = rangenum;
118
119         return NT_STATUS_OK;
120
121 error:
122         return ret;
123
124 }
125
126 NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
127                                        struct autorid_range_config *range,
128                                        bool read_only)
129 {
130         NTSTATUS ret;
131         struct autorid_global_config *globalcfg;
132         fstring keystr;
133
134         /*
135          * try to find mapping without locking the database,
136          * if it is not found create a mapping in a transaction unless
137          * read-only mode has been set
138          */
139         if (range->domain_range_index > 0) {
140                 snprintf(keystr, FSTRING_LEN, "%s#%"PRIu32,
141                          range->domsid, range->domain_range_index);
142         } else {
143                 fstrcpy(keystr, range->domsid);
144         }
145
146         ret = dbwrap_fetch_uint32_bystring(db, keystr,
147                                            &(range->rangenum));
148
149         if (!NT_STATUS_IS_OK(ret)) {
150                 if (read_only) {
151                         return NT_STATUS_NOT_FOUND;
152                 }
153                 ret = dbwrap_trans_do(db,
154                               idmap_autorid_get_domainrange_action, range);
155         }
156
157         ret = idmap_autorid_loadconfig(db, talloc_tos(), &globalcfg);
158         if (!NT_STATUS_IS_OK(ret)) {
159                 return ret;
160         }
161         range->low_id = globalcfg->minvalue
162                       + range->rangenum * globalcfg->rangesize;
163
164         DEBUG(10, ("Using range #%d for domain %s "
165                    "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
166                    range->rangenum, range->domsid, range->domain_range_index,
167                    range->low_id));
168
169         TALLOC_FREE(globalcfg);
170         return ret;
171 }
172
173 /* initialize the given HWM to 0 if it does not exist yet */
174 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
175 {
176         NTSTATUS status;
177         uint32_t hwmval;
178
179         status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
180         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND))  {
181                 status = dbwrap_trans_store_int32_bystring(db, hwm, 0);
182                 if (!NT_STATUS_IS_OK(status)) {
183                         DEBUG(0,
184                               ("Unable to initialise HWM (%s) in autorid "
185                                "database: %s\n", hwm, nt_errstr(status)));
186                         return NT_STATUS_INTERNAL_DB_ERROR;
187                 }
188         } else if (!NT_STATUS_IS_OK(status)) {
189                 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
190                           "database: %s\n", hwm,  nt_errstr(status)));
191                 return status;
192         }
193
194         return NT_STATUS_OK;
195 }
196
197 /*
198  * open and initialize the database which stores the ranges for the domains
199  */
200 NTSTATUS idmap_autorid_db_init(const char *path,
201                                TALLOC_CTX *mem_ctx,
202                                struct db_context **db)
203 {
204         NTSTATUS status;
205
206         if (*db != NULL) {
207                 /* its already open */
208                 return NT_STATUS_OK;
209         }
210
211         /* Open idmap repository */
212         *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
213                       DBWRAP_LOCK_ORDER_1);
214
215         if (*db == NULL) {
216                 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
217                 return NT_STATUS_UNSUCCESSFUL;
218         }
219
220         /* Initialize high water mark for the currently used range to 0 */
221
222         status = idmap_autorid_init_hwm(*db, HWM);
223         NT_STATUS_NOT_OK_RETURN(status);
224
225         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
226         NT_STATUS_NOT_OK_RETURN(status);
227
228         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
229
230         return status;
231 }
232
233 struct idmap_autorid_fetch_config_state {
234         TALLOC_CTX *mem_ctx;
235         char *configstr;
236 };
237
238 static void idmap_autorid_config_parser(TDB_DATA key, TDB_DATA value,
239                                         void *private_data)
240 {
241         struct idmap_autorid_fetch_config_state *state;
242
243         state = (struct idmap_autorid_fetch_config_state *)private_data;
244
245         /*
246          * strndup because we have non-nullterminated strings in the db
247          */
248         state->configstr = talloc_strndup(
249                 state->mem_ctx, (const char *)value.dptr, value.dsize);
250 }
251
252 NTSTATUS idmap_autorid_getconfigstr(struct db_context *db, TALLOC_CTX *mem_ctx,
253                                     char **result)
254 {
255         TDB_DATA key;
256         NTSTATUS status;
257         struct idmap_autorid_fetch_config_state state;
258
259         if (result == NULL) {
260                 return NT_STATUS_INVALID_PARAMETER;
261         }
262
263         key = string_term_tdb_data(CONFIGKEY);
264
265         state.mem_ctx = mem_ctx;
266         state.configstr = NULL;
267
268         status = dbwrap_parse_record(db, key, idmap_autorid_config_parser,
269                                      &state);
270         if (!NT_STATUS_IS_OK(status)) {
271                 DEBUG(1, ("Error while retrieving config: %s\n",
272                           nt_errstr(status)));
273                 return status;
274         }
275
276         if (state.configstr == NULL) {
277                 DEBUG(1, ("Error while retrieving config\n"));
278                 return NT_STATUS_NO_MEMORY;
279         }
280
281         DEBUG(5, ("found CONFIG: %s\n", state.configstr));
282
283         *result = state.configstr;
284         return NT_STATUS_OK;
285 }
286
287 bool idmap_autorid_parse_configstr(const char *configstr,
288                                    struct autorid_global_config *cfg)
289 {
290         unsigned long minvalue, rangesize, maxranges;
291
292         if (sscanf(configstr,
293                    "minvalue:%lu rangesize:%lu maxranges:%lu",
294                    &minvalue, &rangesize, &maxranges) != 3) {
295                 DEBUG(1,
296                       ("Found invalid configuration data"
297                        "creating new config\n"));
298                 return false;
299         }
300
301         cfg->minvalue = minvalue;
302         cfg->rangesize = rangesize;
303         cfg->maxranges = maxranges;
304
305         return true;
306 }
307
308 NTSTATUS idmap_autorid_loadconfig(struct db_context *db,
309                                   TALLOC_CTX *mem_ctx,
310                                   struct autorid_global_config **result)
311 {
312         struct autorid_global_config *cfg;
313         NTSTATUS status;
314         bool ok;
315         char *configstr = NULL;
316
317         if (result == NULL) {
318                 return NT_STATUS_INVALID_PARAMETER;
319         }
320
321         status = idmap_autorid_getconfigstr(db, mem_ctx, &configstr);
322         if (!NT_STATUS_IS_OK(status)) {
323                 return status;
324         }
325
326         cfg = talloc_zero(mem_ctx, struct autorid_global_config);
327         if (cfg == NULL) {
328                 return NT_STATUS_NO_MEMORY;
329         }
330
331         ok = idmap_autorid_parse_configstr(configstr, cfg);
332         if (!ok) {
333                 talloc_free(cfg);
334                 return NT_STATUS_INVALID_PARAMETER;
335         }
336
337         DEBUG(10, ("Loaded previously stored configuration "
338                    "minvalue:%d rangesize:%d\n",
339                    cfg->minvalue, cfg->rangesize));
340
341         *result = cfg;
342
343         return NT_STATUS_OK;
344 }
345
346 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
347                                   struct autorid_global_config *cfg)
348 {
349
350         struct autorid_global_config *storedconfig = NULL;
351         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
352         TDB_DATA data;
353         char *cfgstr;
354         uint32_t hwm;
355         TALLOC_CTX *frame = talloc_stackframe();
356
357         DEBUG(10, ("New configuration provided for storing is "
358                    "minvalue:%d rangesize:%d maxranges:%d\n",
359                    cfg->minvalue, cfg->rangesize, cfg->maxranges));
360
361         if (cfg->rangesize < 2000) {
362                 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
363                 goto done;
364         }
365
366         if (cfg->maxranges == 0) {
367                 DEBUG(1, ("An autorid maxranges value of 0 is invalid. "
368                           "Must have at least one range available.\n"));
369                 goto done;
370         }
371
372         status = idmap_autorid_loadconfig(db, frame, &storedconfig);
373         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
374                 DEBUG(5, ("No configuration found. Storing initial "
375                           "configuration.\n"));
376         } else if (!NT_STATUS_IS_OK(status)) {
377                 goto done;
378         }
379
380         /* did the minimum value or rangesize change? */
381         if (storedconfig &&
382             ((storedconfig->minvalue != cfg->minvalue) ||
383              (storedconfig->rangesize != cfg->rangesize)))
384         {
385                 DEBUG(1, ("New configuration values for rangesize or "
386                           "minimum uid value conflict with previously "
387                           "used values! Not storing new config.\n"));
388                 status = NT_STATUS_INVALID_PARAMETER;
389                 goto done;
390         }
391
392         status = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
393         if (!NT_STATUS_IS_OK(status)) {
394                 DEBUG(1, ("Fatal error while fetching current "
395                           "HWM value: %s\n", nt_errstr(status)));
396                 status = NT_STATUS_INTERNAL_ERROR;
397                 goto done;
398         }
399
400         /*
401          * has the highest uid value been reduced to setting that is not
402          * sufficient any more for already existing ranges?
403          */
404         if (hwm > cfg->maxranges) {
405                 DEBUG(1, ("New upper uid limit is too low to cover "
406                           "existing mappings! Not storing new config.\n"));
407                 status = NT_STATUS_INVALID_PARAMETER;
408                 goto done;
409         }
410
411         cfgstr =
412             talloc_asprintf(frame,
413                             "minvalue:%u rangesize:%u maxranges:%u",
414                             cfg->minvalue, cfg->rangesize, cfg->maxranges);
415
416         if (cfgstr == NULL) {
417                 status = NT_STATUS_NO_MEMORY;
418                 goto done;
419         }
420
421         data = string_tdb_data(cfgstr);
422
423         status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);
424
425 done:
426         TALLOC_FREE(frame);
427         return status;
428 }