idmap_autorid: extract common code to separate file
[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.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
36         range = (struct autorid_range_config *)private_data;
37
38         ret = dbwrap_fetch_uint32_bystring(db, range->keystr,
39                                            &(range->rangenum));
40
41         if (NT_STATUS_IS_OK(ret)) {
42                 /* entry is already present*/
43                 return ret;
44         }
45
46         DEBUG(10, ("Acquiring new range for domain %s "
47                    "(domain_range_index=%"PRIu32")\n",
48                    range->domsid, range->domain_range_index));
49
50         /* fetch the current HWM */
51         ret = dbwrap_fetch_uint32_bystring(db, HWM, &hwm);
52         if (!NT_STATUS_IS_OK(ret)) {
53                 DEBUG(1, ("Fatal error while fetching current "
54                           "HWM value: %s\n", nt_errstr(ret)));
55                 ret = NT_STATUS_INTERNAL_ERROR;
56                 goto error;
57         }
58
59         /* do we have a range left? */
60         if (hwm >= range->globalcfg->maxranges) {
61                 DEBUG(1, ("No more domain ranges available!\n"));
62                 ret = NT_STATUS_NO_MEMORY;
63                 goto error;
64         }
65
66         /* increase the HWM */
67         ret = dbwrap_change_uint32_atomic_bystring(db, HWM, &rangenum, 1);
68         if (!NT_STATUS_IS_OK(ret)) {
69                 DEBUG(1, ("Fatal error while fetching a new "
70                           "domain range value!\n"));
71                 goto error;
72         }
73
74         /* store away the new mapping in both directions */
75         ret = dbwrap_store_uint32_bystring(db, range->keystr, rangenum);
76         if (!NT_STATUS_IS_OK(ret)) {
77                 DEBUG(1, ("Fatal error while storing new "
78                           "domain->range assignment!\n"));
79                 goto error;
80         }
81
82         numstr = talloc_asprintf(db, "%u", rangenum);
83         if (!numstr) {
84                 ret = NT_STATUS_NO_MEMORY;
85                 goto error;
86         }
87
88         ret = dbwrap_store_bystring(db, numstr,
89                         string_term_tdb_data(range->keystr), TDB_INSERT);
90
91         talloc_free(numstr);
92         if (!NT_STATUS_IS_OK(ret)) {
93                 DEBUG(1, ("Fatal error while storing "
94                           "new domain->range assignment!\n"));
95                 goto error;
96         }
97         DEBUG(5, ("Acquired new range #%d for domain %s "
98                   "(domain_range_index=%"PRIu32")\n", rangenum, range->keystr,
99                   range->domain_range_index));
100
101         range->rangenum = rangenum;
102
103         return NT_STATUS_OK;
104
105 error:
106         return ret;
107
108 }
109
110 NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
111                                        struct autorid_range_config *range,
112                                        bool read_only)
113 {
114         NTSTATUS ret;
115
116         /*
117          * try to find mapping without locking the database,
118          * if it is not found create a mapping in a transaction unless
119          * read-only mode has been set
120          */
121         if (range->domain_range_index > 0) {
122                 snprintf(range->keystr, FSTRING_LEN, "%s#%"PRIu32,
123                          range->domsid, range->domain_range_index);
124         } else {
125                 fstrcpy(range->keystr, range->domsid);
126         }
127
128         ret = dbwrap_fetch_uint32_bystring(db, range->keystr,
129                                            &(range->rangenum));
130
131         if (!NT_STATUS_IS_OK(ret)) {
132                 if (read_only) {
133                         return NT_STATUS_NOT_FOUND;
134                 }
135                 ret = dbwrap_trans_do(db,
136                               idmap_autorid_get_domainrange_action, range);
137         }
138
139         range->low_id = range->globalcfg->minvalue
140                       + range->rangenum * range->globalcfg->rangesize;
141
142         DEBUG(10, ("Using range #%d for domain %s "
143                    "(domain_range_index=%"PRIu32", low_id=%"PRIu32")\n",
144                    range->rangenum, range->domsid, range->domain_range_index,
145                    range->low_id));
146
147         return ret;
148 }
149
150 /* initialize the given HWM to 0 if it does not exist yet */
151 NTSTATUS idmap_autorid_init_hwm(struct db_context *db, const char *hwm)
152 {
153         NTSTATUS status;
154         uint32_t hwmval;
155
156         status = dbwrap_fetch_uint32_bystring(db, hwm, &hwmval);
157         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND))  {
158                 status = dbwrap_trans_store_int32_bystring(db, hwm, 0);
159                 if (!NT_STATUS_IS_OK(status)) {
160                         DEBUG(0,
161                               ("Unable to initialise HWM (%s) in autorid "
162                                "database: %s\n", hwm, nt_errstr(status)));
163                         return NT_STATUS_INTERNAL_DB_ERROR;
164                 }
165         } else if (!NT_STATUS_IS_OK(status)) {
166                 DEBUG(0, ("unable to fetch HWM (%s) from autorid "
167                           "database: %s\n", hwm,  nt_errstr(status)));
168                 return status;
169         }
170
171         return NT_STATUS_OK;
172 }
173
174 /*
175  * open and initialize the database which stores the ranges for the domains
176  */
177 NTSTATUS idmap_autorid_db_init(const char *path,
178                                TALLOC_CTX *mem_ctx,
179                                struct db_context **db)
180 {
181         NTSTATUS status;
182
183         if (*db != NULL) {
184                 /* its already open */
185                 return NT_STATUS_OK;
186         }
187
188         /* Open idmap repository */
189         *db = db_open(mem_ctx, path, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644,
190                       DBWRAP_LOCK_ORDER_1);
191
192         if (*db == NULL) {
193                 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n", path));
194                 return NT_STATUS_UNSUCCESSFUL;
195         }
196
197         /* Initialize high water mark for the currently used range to 0 */
198
199         status = idmap_autorid_init_hwm(*db, HWM);
200         NT_STATUS_NOT_OK_RETURN(status);
201
202         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_UID);
203         NT_STATUS_NOT_OK_RETURN(status);
204
205         status = idmap_autorid_init_hwm(*db, ALLOC_HWM_GID);
206
207         return status;
208 }
209
210 struct autorid_global_config *idmap_autorid_loadconfig(struct db_context *db,
211                                                        TALLOC_CTX *ctx)
212 {
213
214         TDB_DATA data;
215         struct autorid_global_config *cfg;
216         unsigned long minvalue, rangesize, maxranges;
217         NTSTATUS status;
218
219         status = dbwrap_fetch_bystring(db, ctx, CONFIGKEY, &data);
220
221         if (!NT_STATUS_IS_OK(status)) {
222                 DEBUG(10, ("No saved config found\n"));
223                 return NULL;
224         }
225
226         cfg = talloc_zero(ctx, struct autorid_global_config);
227         if (!cfg) {
228                 return NULL;
229         }
230
231         if (sscanf((char *)data.dptr,
232                    "minvalue:%lu rangesize:%lu maxranges:%lu",
233                    &minvalue, &rangesize, &maxranges) != 3) {
234                 DEBUG(1,
235                       ("Found invalid configuration data"
236                        "creating new config\n"));
237                 return NULL;
238         }
239
240         cfg->minvalue = minvalue;
241         cfg->rangesize = rangesize;
242         cfg->maxranges = maxranges;
243
244         DEBUG(10, ("Loaded previously stored configuration "
245                    "minvalue:%d rangesize:%d\n",
246                    cfg->minvalue, cfg->rangesize));
247
248         return cfg;
249
250 }
251
252 NTSTATUS idmap_autorid_saveconfig(struct db_context *db,
253                                   struct autorid_global_config *cfg)
254 {
255
256         NTSTATUS status;
257         TDB_DATA data;
258         char *cfgstr;
259
260         cfgstr =
261             talloc_asprintf(talloc_tos(),
262                             "minvalue:%u rangesize:%u maxranges:%u",
263                             cfg->minvalue, cfg->rangesize, cfg->maxranges);
264
265         if (!cfgstr) {
266                 return NT_STATUS_NO_MEMORY;
267         }
268
269         data = string_tdb_data(cfgstr);
270
271         status = dbwrap_trans_store_bystring(db, CONFIGKEY, data, TDB_REPLACE);
272
273         talloc_free(cfgstr);
274
275         return status;
276 }