s3: Fix an uninitialized variable
[kai/samba.git] / source3 / winbindd / idmap_autorid.c
1 /*
2  *  idmap_autorid: static map between Active Directory/NT RIDs
3  *  and RFC 2307 accounts
4  *
5  *  based on the idmap_rid module, but this module defines the ranges
6  *  for the domains by automatically allocating a range for each domain
7  *
8  *  Copyright (C) Christian Ambach, 2010
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 3 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
22  *
23  */
24
25 #include "includes.h"
26 #include "winbindd.h"
27 #include "dbwrap.h"
28 #include "idmap.h"
29 #include "../libcli/security/dom_sid.h"
30
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_IDMAP
33
34 #define HWM "NEXT RANGE"
35 struct autorid_global_config {
36         uint32_t minvalue;
37         uint32_t rangesize;
38         uint32_t maxranges;
39 };
40
41 struct autorid_domain_config {
42         struct dom_sid sid;
43         uint32_t domainnum;
44         struct autorid_global_config *globalcfg;
45 };
46
47 /* handle to the tdb storing domain <-> range assignments */
48 static struct db_context *autorid_db;
49
50 static NTSTATUS idmap_autorid_get_domainrange(struct db_context *db,
51                                               void *private_data)
52 {
53         NTSTATUS ret;
54         uint32_t domainnum, hwm;
55         char *sidstr, *numstr;
56         struct autorid_domain_config *cfg;
57
58         cfg = (struct autorid_domain_config *)private_data;
59         sidstr = dom_sid_string(talloc_tos(), &(cfg->sid));
60
61         if (!sidstr) {
62                 return NT_STATUS_NO_MEMORY;
63         }
64
65         if (!dbwrap_fetch_uint32(db, sidstr, &domainnum)) {
66                 DEBUG(10, ("Acquiring new range for domain %s\n", sidstr));
67
68                 /* fetch the current HWM */
69                 if (!dbwrap_fetch_uint32(db, HWM, &hwm)) {
70                         DEBUG(1, ("Fatal error while fetching current "
71                                   "HWM value!\n"));
72                         ret = NT_STATUS_INTERNAL_ERROR;
73                         goto error;
74                 }
75
76                 /* do we have a range left? */
77                 if (hwm >= cfg->globalcfg->maxranges) {
78                         DEBUG(1, ("No more domain ranges available!\n"));
79                         ret = NT_STATUS_NO_MEMORY;
80                         goto error;
81                 }
82
83                 /* increase the HWM */
84                 ret = dbwrap_change_uint32_atomic(db, HWM, &domainnum, 1);
85                 if (!NT_STATUS_IS_OK(ret)) {
86                         DEBUG(1, ("Fatal error while fetching a new "
87                                   "domain range value!\n"));
88                         goto error;
89                 }
90
91                 /* store away the new mapping in both directions */
92                 ret = dbwrap_trans_store_uint32(db, sidstr, domainnum);
93                 if (!NT_STATUS_IS_OK(ret)) {
94                         DEBUG(1, ("Fatal error while storing new "
95                                   "domain->range assignment!\n"));
96                         goto error;
97                 }
98
99                 numstr = talloc_asprintf(db, "%u", domainnum);
100                 if (!numstr) {
101                         ret = NT_STATUS_NO_MEMORY;
102                         goto error;
103                 }
104
105                 ret = dbwrap_trans_store_bystring(db, numstr,
106                                                   string_term_tdb_data(sidstr),
107                                                   TDB_INSERT);
108                 if (!NT_STATUS_IS_OK(ret)) {
109                         talloc_free(numstr);
110                         DEBUG(1, ("Fatal error while storing "
111                                   "new domain->range assignment!\n"));
112                         goto error;
113                 }
114                 talloc_free(numstr);
115                 DEBUG(5, ("Acquired new range #%d for domain %s\n",
116                           domainnum, sidstr));
117         }
118
119         DEBUG(10, ("Using range #%d for domain %s\n", domainnum, sidstr));
120         cfg->domainnum = domainnum;
121
122         talloc_free(sidstr);
123         return NT_STATUS_OK;
124
125       error:
126         talloc_free(sidstr);
127         return ret;
128
129 }
130
131 static NTSTATUS idmap_autorid_id_to_sid(TALLOC_CTX * memctx,
132                                         struct autorid_global_config *cfg,
133                                         struct id_map *map)
134 {
135         uint32_t range;
136         TDB_DATA data;
137         char *keystr;
138         struct dom_sid sid;
139
140         /* can this be one of our ids? */
141         if (map->xid.id < cfg->minvalue) {
142                 DEBUG(10, ("id %d is lower than minimum value, "
143                            "ignoring mapping request\n", map->xid.id));
144                 map->status = ID_UNKNOWN;
145                 return NT_STATUS_OK;
146         }
147
148         if (map->xid.id > (cfg->minvalue + cfg->rangesize * cfg->maxranges)) {
149                 DEBUG(10, ("id %d is outside of maximum id value, "
150                            "ignoring mapping request\n", map->xid.id));
151                 map->status = ID_UNKNOWN;
152                 return NT_STATUS_OK;
153         }
154
155         /* determine the range of this uid */
156         range = ((map->xid.id - cfg->minvalue) / cfg->rangesize);
157
158         keystr = talloc_asprintf(memctx, "%u", range);
159         if (!keystr) {
160                 return NT_STATUS_NO_MEMORY;
161         }
162
163         data = dbwrap_fetch_bystring(autorid_db, memctx, keystr);
164
165         if (!data.dptr) {
166                 DEBUG(4, ("id %d belongs to range %d which does not have "
167                           "domain mapping, ignoring mapping request\n",
168                           map->xid.id, range));
169         } else {
170                 string_to_sid(&sid, (const char *)data.dptr);
171
172                 sid_compose(map->sid, &sid,
173                             (map->xid.id - cfg->minvalue -
174                              range * cfg->rangesize));
175
176                 /* We **really** should have some way of validating
177                    the SID exists and is the correct type here.  But
178                    that is a deficiency in the idmap_rid design. */
179
180                 map->status = ID_MAPPED;
181         }
182         return NT_STATUS_OK;
183 }
184
185 /**********************************
186  Single sid to id lookup function.
187 **********************************/
188
189 static NTSTATUS idmap_autorid_sid_to_id(TALLOC_CTX * memctx,
190                                         struct autorid_global_config *global,
191                                         struct autorid_domain_config *domain,
192                                         struct id_map *map)
193 {
194         uint32_t rid;
195
196         sid_peek_rid(map->sid, &rid);
197
198         /* if the rid is higher than the size of the range, we cannot map it */
199         if (rid >= global->rangesize) {
200                 map->status = ID_UNKNOWN;
201                 DEBUG(2, ("RID %d is larger then size of range (%d), "
202                           "user cannot be mapped\n", rid, global->rangesize));
203                 return NT_STATUS_UNSUCCESSFUL;
204         }
205         map->xid.id = global->minvalue +
206             (global->rangesize * domain->domainnum)+rid;
207
208         /* We **really** should have some way of validating
209            the SID exists and is the correct type here.  But
210            that is a deficiency in the idmap_rid design. */
211
212         map->status = ID_MAPPED;
213
214         return NT_STATUS_OK;
215 }
216
217 /**********************************
218  lookup a set of unix ids.
219 **********************************/
220
221 static NTSTATUS idmap_autorid_unixids_to_sids(struct idmap_domain *dom,
222                                               struct id_map **ids)
223 {
224         struct autorid_global_config *globalcfg;
225         TALLOC_CTX *ctx;
226         NTSTATUS ret;
227         int i;
228
229         /* initialize the status to avoid surprise */
230         for (i = 0; ids[i]; i++) {
231                 ids[i]->status = ID_UNKNOWN;
232         }
233
234         globalcfg = talloc_get_type(dom->private_data,
235                                     struct autorid_global_config);
236
237         ctx = talloc_new(dom);
238         if (!ctx) {
239                 DEBUG(0, ("Out of memory!\n"));
240                 return NT_STATUS_NO_MEMORY;
241         }
242
243         for (i = 0; ids[i]; i++) {
244
245                 ret = idmap_autorid_id_to_sid(ctx, globalcfg, ids[i]);
246
247                 if ((!NT_STATUS_IS_OK(ret)) &&
248                     (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
249                         /* some fatal error occurred, log it */
250                         DEBUG(3, ("Unexpected error resolving an ID "
251                                   " (%d)\n", ids[i]->xid.id));
252                         goto failure;
253                 }
254         }
255
256         talloc_free(ctx);
257         return NT_STATUS_OK;
258
259       failure:
260         talloc_free(ctx);
261         return ret;
262
263 }
264
265 /**********************************
266  lookup a set of sids.
267 **********************************/
268
269 static NTSTATUS idmap_autorid_sids_to_unixids(struct idmap_domain *dom,
270                                               struct id_map **ids)
271 {
272         struct autorid_global_config *global;
273         struct autorid_domain_config *domaincfg;
274         struct dom_sid *domainsid;
275         struct winbindd_tdc_domain *domain;
276         uint32_t rid;
277         TALLOC_CTX *ctx;
278         NTSTATUS ret;
279         int i;
280
281         ctx = talloc_new(dom);
282         domainsid = talloc(ctx, struct dom_sid);
283         if (!ctx || !domainsid) {
284                 DEBUG(0, ("Out of memory!\n"));
285                 ret = NT_STATUS_NO_MEMORY;
286                 goto failure;
287         }
288
289         if ((domaincfg = TALLOC_ZERO_P(ctx,
290                                        struct autorid_domain_config)) == NULL) {
291                 DEBUG(0, ("Out of memory!\n"));
292                 ret = NT_STATUS_NO_MEMORY;
293                 goto failure;
294         }
295
296         /* initialize the status to avoid surprise */
297         for (i = 0; ids[i]; i++) {
298                 ids[i]->status = ID_UNKNOWN;
299         }
300
301         global = talloc_get_type(dom->private_data,
302                                  struct autorid_global_config);
303
304         for (i = 0; ids[i]; i++) {
305                 ZERO_STRUCTP(domainsid);
306                 sid_copy(domainsid, ids[i]->sid);
307                 if (!sid_split_rid(domainsid, &rid)) {
308                         DEBUG(4, ("Could not determine domain SID from %s, "
309                                   "ignoring mapping request\n",
310                                   sid_string_dbg(ids[i]->sid)));
311                         continue;
312                 }
313
314                 domain = wcache_tdc_fetch_domainbysid(ctx, domainsid);
315                 if (domain == NULL) {
316                         DEBUG(10, ("Ignoring unknown domain sid %s\n",
317                                    sid_string_dbg(domainsid)));
318                         continue;
319                 }
320
321                 ZERO_STRUCTP(domaincfg);
322                 domaincfg->sid = domain->sid;
323                 domaincfg->globalcfg = global;
324
325                 ret = dbwrap_trans_do(autorid_db, idmap_autorid_get_domainrange,
326                                       domaincfg);
327
328                 if (!NT_STATUS_IS_OK(ret)) {
329                         DEBUG(3, ("Could not determine range for domain, "
330                                   "check previous messages for reason\n"));
331                         goto failure;
332                 }
333
334                 ret = idmap_autorid_sid_to_id(ctx, global, domaincfg, ids[i]);
335
336                 if ((!NT_STATUS_IS_OK(ret)) &&
337                     (!NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED))) {
338                         /* some fatal error occurred, log it */
339                         DEBUG(3, ("Unexpected error resolving a SID (%s)\n",
340                                   sid_string_dbg(ids[i]->sid)));
341                         goto failure;
342                 }
343         }
344
345         talloc_free(ctx);
346         return NT_STATUS_OK;
347
348       failure:
349         talloc_free(ctx);
350         return ret;
351
352 }
353
354 /*
355  * open and initialize the database which stores the ranges for the domains
356  */
357 static NTSTATUS idmap_autorid_db_init(void)
358 {
359         int32_t hwm;
360
361         if (autorid_db) {
362                 /* its already open */
363                 return NT_STATUS_OK;
364         }
365
366         /* Open idmap repository */
367         autorid_db = db_open(NULL, state_path("autorid.tdb"), 0,
368                              TDB_DEFAULT, O_RDWR | O_CREAT, 0644);
369
370         if (!autorid_db) {
371                 DEBUG(0, ("Unable to open idmap_autorid database '%s'\n",
372                           state_path("autorid.tdb")));
373                 return NT_STATUS_UNSUCCESSFUL;
374         }
375
376         /* Initialize high water mark for the currently used range to 0 */
377         hwm = dbwrap_fetch_int32(autorid_db, HWM);
378         if ((hwm < 0)) {
379                 if (!NT_STATUS_IS_OK
380                     (dbwrap_trans_store_int32(autorid_db, HWM, 0))) {
381                         DEBUG(0,
382                               ("Unable to initialise HWM in autorid "
383                                "database\n"));
384                         return NT_STATUS_INTERNAL_DB_ERROR;
385                 }
386         }
387
388         return NT_STATUS_OK;
389 }
390
391 static NTSTATUS idmap_autorid_initialize(struct idmap_domain *dom,
392                                          const char *params)
393 {
394         struct autorid_global_config *config;
395         NTSTATUS status;
396
397         config = TALLOC_ZERO_P(dom, struct autorid_global_config);
398         if (!config) {
399                 DEBUG(0, ("Out of memory!\n"));
400                 return NT_STATUS_NO_MEMORY;
401         }
402
403         status = idmap_autorid_db_init();
404         if (!NT_STATUS_IS_OK(status)) {
405                 goto error;
406         }
407
408         config->minvalue = dom->low_id;
409         config->rangesize = lp_parm_int(-1, "autorid", "rangesize", 100000);
410
411         if (config->rangesize < 2000) {
412                 DEBUG(1, ("autorid rangesize must be at least 2000\n"));
413                 status = NT_STATUS_INVALID_PARAMETER;
414                 goto error;
415         }
416
417         config->maxranges = (dom->high_id - dom->low_id + 1) /
418             config->rangesize;
419
420         if (config->maxranges == 0) {
421                 DEBUG(1, ("allowed uid range is smaller then rangesize, "
422                           "increase uid range or decrease rangesize\n"));
423                 status = NT_STATUS_INVALID_PARAMETER;
424                 goto error;
425         }
426
427         /* check if the high-low limit is a multiple of the rangesize */
428         if ((dom->high_id - dom->low_id + 1) % config->rangesize != 0) {
429                 DEBUG(5, ("High uid-low uid difference of %d "
430                           "is not a multiple of the rangesize %d, "
431                           "limiting ranges to lower boundary number of %d\n",
432                           (dom->high_id - dom->low_id + 1), config->rangesize,
433                           config->maxranges));
434         }
435
436         DEBUG(5, ("%d domain ranges with a size of %d are available\n",
437                   config->maxranges, config->rangesize));
438
439         dom->private_data = config;
440
441         if (!NT_STATUS_IS_OK(status)) {
442                 goto error;
443         }
444
445         return NT_STATUS_OK;
446
447       error:
448         talloc_free(config);
449         return status;
450 }
451
452 /*
453   Close the idmap tdb instance
454 */
455 static NTSTATUS idmap_autorid_close(struct idmap_domain *dom)
456 {
457         /* don't do anything */
458         return NT_STATUS_OK;
459 }
460
461 static struct idmap_methods autorid_methods = {
462         .init = idmap_autorid_initialize,
463         .unixids_to_sids = idmap_autorid_unixids_to_sids,
464         .sids_to_unixids = idmap_autorid_sids_to_unixids,
465         .close_fn = idmap_autorid_close
466 };
467
468 NTSTATUS idmap_autorid_init(void)
469 {
470         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION,
471                                   "autorid", &autorid_methods);
472 }