s3: fix the build of idmap_adex, idmap_tdb2, idmap_hash and several vfs modules.
[anatoliy/anatoliy.git] / source3 / winbindd / idmap_tdb2.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    idmap TDB2 backend, used for clustered Samba setups.
5
6    This uses dbwrap to access tdb files. The location can be set
7    using tdb:idmap2.tdb =" in smb.conf
8
9    Copyright (C) Andrew Tridgell 2007
10
11    This is heavily based upon idmap_tdb.c, which is:
12
13    Copyright (C) Tim Potter 2000
14    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
15    Copyright (C) Jeremy Allison 2006
16    Copyright (C) Simo Sorce 2003-2006
17    Copyright (C) Michael Adam 2009-2010
18
19    This program is free software; you can redistribute it and/or modify
20    it under the terms of the GNU General Public License as published by
21    the Free Software Foundation; either version 2 of the License, or
22    (at your option) any later version.
23
24    This program is distributed in the hope that it will be useful,
25    but WITHOUT ANY WARRANTY; without even the implied warranty of
26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27    GNU General Public License for more details.
28
29    You should have received a copy of the GNU General Public License
30    along with this program; if not, write to the Free Software
31    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32 */
33
34 #include "includes.h"
35 #include "winbindd.h"
36 #include "idmap.h"
37 #include "idmap_rw.h"
38 #include "dbwrap.h"
39 #include "../libcli/security/dom_sid.h"
40
41 #undef DBGC_CLASS
42 #define DBGC_CLASS DBGC_IDMAP
43
44 struct idmap_tdb2_context {
45         struct db_context *db;
46         const char *script; /* script to provide idmaps */
47         struct idmap_rw_ops *rw_ops;
48 };
49
50 /* High water mark keys */
51 #define HWM_GROUP  "GROUP HWM"
52 #define HWM_USER   "USER HWM"
53
54
55 /*
56  * check and initialize high/low water marks in the db
57  */
58 static NTSTATUS idmap_tdb2_init_hwm(struct idmap_domain *dom)
59 {
60         uint32 low_id;
61         struct idmap_tdb2_context *ctx;
62
63         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
64
65         /* Create high water marks for group and user id */
66
67         low_id = dbwrap_fetch_int32(ctx->db, HWM_USER);
68         if ((low_id == -1) || (low_id < dom->low_id)) {
69                 if (!NT_STATUS_IS_OK(dbwrap_trans_store_int32(
70                                              ctx->db, HWM_USER,
71                                              dom->low_id))) {
72                         DEBUG(0, ("Unable to initialise user hwm in idmap "
73                                   "database\n"));
74                         return NT_STATUS_INTERNAL_DB_ERROR;
75                 }
76         }
77
78         low_id = dbwrap_fetch_int32(ctx->db, HWM_GROUP);
79         if ((low_id == -1) || (low_id < dom->low_id)) {
80                 if (!NT_STATUS_IS_OK(dbwrap_trans_store_int32(
81                                              ctx->db, HWM_GROUP,
82                                              dom->low_id))) {
83                         DEBUG(0, ("Unable to initialise group hwm in idmap "
84                                   "database\n"));
85                         return NT_STATUS_INTERNAL_DB_ERROR;
86                 }
87         }
88
89         return NT_STATUS_OK;
90 }
91
92
93 /*
94   open the permanent tdb
95  */
96 static NTSTATUS idmap_tdb2_open_db(struct idmap_domain *dom)
97 {
98         char *db_path;
99         struct idmap_tdb2_context *ctx;
100
101         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
102
103         if (ctx->db) {
104                 /* its already open */
105                 return NT_STATUS_OK;
106         }
107
108         db_path = lp_parm_talloc_string(-1, "tdb", "idmap2.tdb", NULL);
109         if (db_path == NULL) {
110                 /* fall back to the private directory, which, despite
111                    its name, is usually on shared storage */
112                 db_path = talloc_asprintf(NULL, "%s/idmap2.tdb", lp_private_dir());
113         }
114         NT_STATUS_HAVE_NO_MEMORY(db_path);
115
116         /* Open idmap repository */
117         ctx->db = db_open(ctx, db_path, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0644);
118         TALLOC_FREE(db_path);
119
120         if (ctx->db == NULL) {
121                 DEBUG(0, ("Unable to open idmap_tdb2 database '%s'\n",
122                           db_path));
123                 return NT_STATUS_UNSUCCESSFUL;
124         }
125
126         return idmap_tdb2_init_hwm(dom);
127 }
128
129
130 /*
131   Allocate a new id. 
132 */
133
134 struct idmap_tdb2_allocate_id_context {
135         const char *hwmkey;
136         const char *hwmtype;
137         uint32_t high_hwm;
138         uint32_t hwm;
139 };
140
141 static NTSTATUS idmap_tdb2_allocate_id_action(struct db_context *db,
142                                               void *private_data)
143 {
144         NTSTATUS ret;
145         struct idmap_tdb2_allocate_id_context *state;
146         uint32_t hwm;
147
148         state = (struct idmap_tdb2_allocate_id_context *)private_data;
149
150         hwm = dbwrap_fetch_int32(db, state->hwmkey);
151         if (hwm == -1) {
152                 ret = NT_STATUS_INTERNAL_DB_ERROR;
153                 goto done;
154         }
155
156         /* check it is in the range */
157         if (hwm > state->high_hwm) {
158                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
159                           state->hwmtype, (unsigned long)state->high_hwm));
160                 ret = NT_STATUS_UNSUCCESSFUL;
161                 goto done;
162         }
163
164         /* fetch a new id and increment it */
165         ret = dbwrap_trans_change_uint32_atomic(db, state->hwmkey, &hwm, 1);
166         if (!NT_STATUS_IS_OK(ret)) {
167                 DEBUG(1, ("Fatal error while fetching a new %s value\n!",
168                           state->hwmtype));
169                 goto done;
170         }
171
172         /* recheck it is in the range */
173         if (hwm > state->high_hwm) {
174                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
175                           state->hwmtype, (unsigned long)state->high_hwm));
176                 ret = NT_STATUS_UNSUCCESSFUL;
177                 goto done;
178         }
179
180         ret = NT_STATUS_OK;
181         state->hwm = hwm;
182
183 done:
184         return ret;
185 }
186
187 static NTSTATUS idmap_tdb2_allocate_id(struct idmap_domain *dom,
188                                        struct unixid *xid)
189 {
190         const char *hwmkey;
191         const char *hwmtype;
192         uint32_t high_hwm;
193         uint32_t hwm = 0;
194         NTSTATUS status;
195         struct idmap_tdb2_allocate_id_context state;
196         struct idmap_tdb2_context *ctx;
197
198         status = idmap_tdb2_open_db(dom);
199         NT_STATUS_NOT_OK_RETURN(status);
200
201         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
202
203         /* Get current high water mark */
204         switch (xid->type) {
205
206         case ID_TYPE_UID:
207                 hwmkey = HWM_USER;
208                 hwmtype = "UID";
209                 break;
210
211         case ID_TYPE_GID:
212                 hwmkey = HWM_GROUP;
213                 hwmtype = "GID";
214                 break;
215
216         default:
217                 DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type));
218                 return NT_STATUS_INVALID_PARAMETER;
219         }
220
221         high_hwm = dom->high_id;
222
223         state.hwm = hwm;
224         state.high_hwm = high_hwm;
225         state.hwmtype = hwmtype;
226         state.hwmkey = hwmkey;
227
228         status = dbwrap_trans_do(ctx->db, idmap_tdb2_allocate_id_action,
229                                  &state);
230
231         if (NT_STATUS_IS_OK(status)) {
232                 xid->id = state.hwm;
233                 DEBUG(10,("New %s = %d\n", hwmtype, state.hwm));
234         } else {
235                 DEBUG(1, ("Error allocating a new %s\n", hwmtype));
236         }
237
238         return status;
239 }
240
241 /**
242  * Allocate a new unix-ID.
243  * For now this is for the default idmap domain only.
244  * Should be extended later on.
245  */
246 static NTSTATUS idmap_tdb2_get_new_id(struct idmap_domain *dom,
247                                       struct unixid *id)
248 {
249         NTSTATUS ret;
250
251         if (!strequal(dom->name, "*")) {
252                 DEBUG(3, ("idmap_tdb2_get_new_id: "
253                           "Refusing creation of mapping for domain'%s'. "
254                           "Currently only supported for the default "
255                           "domain \"*\".\n",
256                            dom->name));
257                 return NT_STATUS_NOT_IMPLEMENTED;
258         }
259
260         ret = idmap_tdb2_allocate_id(dom, id);
261
262         return ret;
263 }
264
265 /*
266   IDMAP MAPPING TDB BACKEND
267 */
268
269 static NTSTATUS idmap_tdb2_set_mapping(struct idmap_domain *dom,
270                                        const struct id_map *map);
271
272 /*
273   Initialise idmap database. 
274 */
275 static NTSTATUS idmap_tdb2_db_init(struct idmap_domain *dom,
276                                    const char *params)
277 {
278         NTSTATUS ret;
279         struct idmap_tdb2_context *ctx;
280
281         ctx = talloc_zero(dom, struct idmap_tdb2_context);
282         if ( ! ctx) {
283                 DEBUG(0, ("Out of memory!\n"));
284                 return NT_STATUS_NO_MEMORY;
285         }
286
287         if (strequal(dom->name, "*")) {
288                 ctx->script = lp_parm_const_string(-1, "idmap", "script", NULL);
289                 if (ctx->script) {
290                         DEBUG(1, ("using idmap script '%s'\n", ctx->script));
291                 }
292         } else {
293                 char *config_option = NULL;
294
295                 config_option = talloc_asprintf(ctx, "idmap config %s", dom->name);
296                 if ( ! config_option) {
297                         DEBUG(0, ("Out of memory!\n"));
298                         ret = NT_STATUS_NO_MEMORY;
299                         goto failed;
300                 }
301
302                 ctx->script = lp_parm_const_string(-1, config_option, "script", NULL);
303                 if (ctx->script) {
304                         DEBUG(1, ("using idmap script '%s'\n", ctx->script));
305                 }
306
307                 talloc_free(config_option);
308         }
309
310         ctx->rw_ops = talloc_zero(ctx, struct idmap_rw_ops);
311         if (ctx->rw_ops == NULL) {
312                 DEBUG(0, ("Out of memory!\n"));
313                 ret = NT_STATUS_NO_MEMORY;
314                 goto failed;
315         }
316
317         ctx->rw_ops->get_new_id = idmap_tdb2_get_new_id;
318         ctx->rw_ops->set_mapping = idmap_tdb2_set_mapping;
319
320         dom->private_data = ctx;
321
322         ret = idmap_tdb2_open_db(dom);
323         if (!NT_STATUS_IS_OK(ret)) {
324                 goto failed;
325         }
326
327         return NT_STATUS_OK;
328
329 failed:
330         talloc_free(ctx);
331         return ret;
332 }
333
334
335 /**
336  * store a mapping in the database.
337  */
338
339 struct idmap_tdb2_set_mapping_context {
340         const char *ksidstr;
341         const char *kidstr;
342 };
343
344 static NTSTATUS idmap_tdb2_set_mapping_action(struct db_context *db,
345                                               void *private_data)
346 {
347         TDB_DATA data;
348         NTSTATUS ret;
349         struct idmap_tdb2_set_mapping_context *state;
350         TALLOC_CTX *tmp_ctx = talloc_stackframe();
351
352         state = (struct idmap_tdb2_set_mapping_context *)private_data;
353
354         DEBUG(10, ("Storing %s <-> %s map\n", state->ksidstr, state->kidstr));
355
356         /* check wheter sid mapping is already present in db */
357         data = dbwrap_fetch_bystring(db, tmp_ctx, state->ksidstr);
358         if (data.dptr) {
359                 ret = NT_STATUS_OBJECT_NAME_COLLISION;
360                 goto done;
361         }
362
363         ret = dbwrap_store_bystring(db, state->ksidstr,
364                                     string_term_tdb_data(state->kidstr),
365                                     TDB_INSERT);
366         if (!NT_STATUS_IS_OK(ret)) {
367                 DEBUG(0, ("Error storing SID -> ID: %s\n", nt_errstr(ret)));
368                 goto done;
369         }
370
371         ret = dbwrap_store_bystring(db, state->kidstr,
372                                     string_term_tdb_data(state->ksidstr),
373                                     TDB_INSERT);
374         if (!NT_STATUS_IS_OK(ret)) {
375                 DEBUG(0, ("Error storing ID -> SID: %s\n", nt_errstr(ret)));
376                 /* try to remove the previous stored SID -> ID map */
377                 dbwrap_delete_bystring(db, state->ksidstr);
378                 goto done;
379         }
380
381         DEBUG(10,("Stored %s <-> %s\n", state->ksidstr, state->kidstr));
382
383 done:
384         talloc_free(tmp_ctx);
385         return ret;
386 }
387
388 static NTSTATUS idmap_tdb2_set_mapping(struct idmap_domain *dom, const struct id_map *map)
389 {
390         struct idmap_tdb2_context *ctx;
391         NTSTATUS ret;
392         char *ksidstr, *kidstr;
393         struct idmap_tdb2_set_mapping_context state;
394
395         if (!map || !map->sid) {
396                 return NT_STATUS_INVALID_PARAMETER;
397         }
398
399         ksidstr = kidstr = NULL;
400
401         /* TODO: should we filter a set_mapping using low/high filters ? */
402
403         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
404
405         switch (map->xid.type) {
406
407         case ID_TYPE_UID:
408                 kidstr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
409                 break;
410
411         case ID_TYPE_GID:
412                 kidstr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
413                 break;
414
415         default:
416                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
417                 return NT_STATUS_INVALID_PARAMETER;
418         }
419
420         if (kidstr == NULL) {
421                 DEBUG(0, ("ERROR: Out of memory!\n"));
422                 ret = NT_STATUS_NO_MEMORY;
423                 goto done;
424         }
425
426         ksidstr = sid_string_talloc(ctx, map->sid);
427         if (ksidstr == NULL) {
428                 DEBUG(0, ("Out of memory!\n"));
429                 ret = NT_STATUS_NO_MEMORY;
430                 goto done;
431         }
432
433         state.ksidstr = ksidstr;
434         state.kidstr = kidstr;
435
436         ret = dbwrap_trans_do(ctx->db, idmap_tdb2_set_mapping_action,
437                               &state);
438
439 done:
440         talloc_free(ksidstr);
441         talloc_free(kidstr);
442         return ret;
443 }
444
445 /**
446  * Create a new mapping for an unmapped SID, also allocating a new ID.
447  * This should be run inside a transaction.
448  *
449  * TODO:
450 *  Properly integrate this with multi domain idmap config:
451  * Currently, the allocator is default-config only.
452  */
453 static NTSTATUS idmap_tdb2_new_mapping(struct idmap_domain *dom, struct id_map *map)
454 {
455         NTSTATUS ret;
456         struct idmap_tdb2_context *ctx;
457
458         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
459
460         ret = idmap_rw_new_mapping(dom, ctx->rw_ops, map);
461
462         return ret;
463 }
464
465
466 /*
467   run a script to perform a mapping
468
469   The script should the following command lines:
470
471       SIDTOID S-1-xxxx
472       IDTOSID UID xxxx
473       IDTOSID GID xxxx
474
475   and should return one of the following as a single line of text
476      UID:xxxx
477      GID:xxxx
478      SID:xxxx
479      ERR:xxxx
480  */
481 static NTSTATUS idmap_tdb2_script(struct idmap_tdb2_context *ctx, struct id_map *map,
482                                   const char *fmt, ...)
483 {
484         va_list ap;
485         char *cmd;
486         FILE *p;
487         char line[64];
488         unsigned long v;
489
490         cmd = talloc_asprintf(ctx, "%s ", ctx->script);
491         NT_STATUS_HAVE_NO_MEMORY(cmd);  
492
493         va_start(ap, fmt);
494         cmd = talloc_vasprintf_append(cmd, fmt, ap);
495         va_end(ap);
496         NT_STATUS_HAVE_NO_MEMORY(cmd);
497
498         p = popen(cmd, "r");
499         talloc_free(cmd);
500         if (p == NULL) {
501                 return NT_STATUS_NONE_MAPPED;
502         }
503
504         if (fgets(line, sizeof(line)-1, p) == NULL) {
505                 pclose(p);
506                 return NT_STATUS_NONE_MAPPED;
507         }
508         pclose(p);
509
510         DEBUG(10,("idmap script gave: %s\n", line));
511
512         if (sscanf(line, "UID:%lu", &v) == 1) {
513                 map->xid.id   = v;
514                 map->xid.type = ID_TYPE_UID;
515         } else if (sscanf(line, "GID:%lu", &v) == 1) {
516                 map->xid.id   = v;
517                 map->xid.type = ID_TYPE_GID;            
518         } else if (strncmp(line, "SID:S-", 6) == 0) {
519                 if (!string_to_sid(map->sid, &line[4])) {
520                         DEBUG(0,("Bad SID in '%s' from idmap script %s\n",
521                                  line, ctx->script));
522                         return NT_STATUS_NONE_MAPPED;                   
523                 }
524         } else {
525                 DEBUG(0,("Bad reply '%s' from idmap script %s\n",
526                          line, ctx->script));
527                 return NT_STATUS_NONE_MAPPED;
528         }
529
530         return NT_STATUS_OK;
531 }
532
533
534
535 /*
536   Single id to sid lookup function. 
537 */
538 static NTSTATUS idmap_tdb2_id_to_sid(struct idmap_domain *dom, struct id_map *map)
539 {
540         NTSTATUS ret;
541         TDB_DATA data;
542         char *keystr;
543         NTSTATUS status;
544         struct idmap_tdb2_context *ctx;
545
546
547         if (!dom || !map) {
548                 return NT_STATUS_INVALID_PARAMETER;
549         }
550
551         status = idmap_tdb2_open_db(dom);
552         NT_STATUS_NOT_OK_RETURN(status);
553
554         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
555
556         /* apply filters before checking */
557         if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
558                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
559                                 map->xid.id, dom->low_id, dom->high_id));
560                 return NT_STATUS_NONE_MAPPED;
561         }
562
563         switch (map->xid.type) {
564
565         case ID_TYPE_UID:
566                 keystr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
567                 break;
568
569         case ID_TYPE_GID:
570                 keystr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
571                 break;
572
573         default:
574                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
575                 return NT_STATUS_INVALID_PARAMETER;
576         }
577
578         /* final SAFE_FREE safe */
579         data.dptr = NULL;
580
581         if (keystr == NULL) {
582                 DEBUG(0, ("Out of memory!\n"));
583                 ret = NT_STATUS_NO_MEMORY;
584                 goto done;
585         }
586
587         DEBUG(10,("Fetching record %s\n", keystr));
588
589         /* Check if the mapping exists */
590         data = dbwrap_fetch_bystring(ctx->db, keystr, keystr);
591
592         if (!data.dptr) {
593                 char *sidstr;
594                 struct idmap_tdb2_set_mapping_context store_state;
595
596                 DEBUG(10,("Record %s not found\n", keystr));
597                 if (ctx->script == NULL) {
598                         ret = NT_STATUS_NONE_MAPPED;
599                         goto done;
600                 }
601
602                 ret = idmap_tdb2_script(ctx, map, "IDTOSID %s", keystr);
603
604                 /* store it on shared storage */
605                 if (!NT_STATUS_IS_OK(ret)) {
606                         goto done;
607                 }
608
609                 sidstr = sid_string_talloc(keystr, map->sid);
610                 if (!sidstr) {
611                         ret = NT_STATUS_NO_MEMORY;
612                         goto done;
613                 }
614
615                 store_state.ksidstr = sidstr;
616                 store_state.kidstr = keystr;
617
618                 ret = dbwrap_trans_do(ctx->db, idmap_tdb2_set_mapping_action,
619                                       &store_state);
620                 goto done;
621         }
622
623         if (!string_to_sid(map->sid, (const char *)data.dptr)) {
624                 DEBUG(10,("INVALID SID (%s) in record %s\n",
625                         (const char *)data.dptr, keystr));
626                 ret = NT_STATUS_INTERNAL_DB_ERROR;
627                 goto done;
628         }
629
630         DEBUG(10,("Found record %s -> %s\n", keystr, (const char *)data.dptr));
631         ret = NT_STATUS_OK;
632
633 done:
634         talloc_free(keystr);
635         return ret;
636 }
637
638
639 /*
640  Single sid to id lookup function. 
641 */
642 static NTSTATUS idmap_tdb2_sid_to_id(struct idmap_domain *dom, struct id_map *map)
643 {
644         NTSTATUS ret;
645         TDB_DATA data;
646         char *keystr;
647         unsigned long rec_id = 0;
648         struct idmap_tdb2_context *ctx;
649         TALLOC_CTX *tmp_ctx = talloc_stackframe();
650
651         ret = idmap_tdb2_open_db(dom);
652         NT_STATUS_NOT_OK_RETURN(ret);
653
654         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
655
656         keystr = sid_string_talloc(tmp_ctx, map->sid);
657         if (keystr == NULL) {
658                 DEBUG(0, ("Out of memory!\n"));
659                 ret = NT_STATUS_NO_MEMORY;
660                 goto done;
661         }
662
663         DEBUG(10,("Fetching record %s\n", keystr));
664
665         /* Check if sid is present in database */
666         data = dbwrap_fetch_bystring(ctx->db, tmp_ctx, keystr);
667         if (!data.dptr) {
668                 char *idstr;
669                 struct idmap_tdb2_set_mapping_context store_state;
670
671                 DEBUG(10,(__location__ " Record %s not found\n", keystr));
672
673                 if (ctx->script == NULL) {
674                         ret = NT_STATUS_NONE_MAPPED;
675                         goto done;
676                 }
677
678                 ret = idmap_tdb2_script(ctx, map, "SIDTOID %s", keystr);
679                 /* store it on shared storage */
680                 if (!NT_STATUS_IS_OK(ret)) {
681                         goto done;
682                 }
683
684                 /* apply filters before returning result */
685                 if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
686                         DEBUG(5, ("Script returned id (%u) out of range "
687                                   "(%u - %u). Filtered!\n",
688                                   map->xid.id, dom->low_id, dom->high_id));
689                         ret = NT_STATUS_NONE_MAPPED;
690                         goto done;
691                 }
692
693                 idstr = talloc_asprintf(tmp_ctx, "%cID %lu",
694                                         map->xid.type == ID_TYPE_UID?'U':'G',
695                                         (unsigned long)map->xid.id);
696                 if (idstr == NULL) {
697                         ret = NT_STATUS_NO_MEMORY;
698                         goto done;
699                 }
700
701                 store_state.ksidstr = keystr;
702                 store_state.kidstr = idstr;
703
704                 ret = dbwrap_trans_do(ctx->db, idmap_tdb2_set_mapping_action,
705                                       &store_state);
706                 goto done;
707         }
708
709         /* What type of record is this ? */
710         if (sscanf((const char *)data.dptr, "UID %lu", &rec_id) == 1) { /* Try a UID record. */
711                 map->xid.id = rec_id;
712                 map->xid.type = ID_TYPE_UID;
713                 DEBUG(10,("Found uid record %s -> %s \n", keystr, (const char *)data.dptr ));
714                 ret = NT_STATUS_OK;
715
716         } else if (sscanf((const char *)data.dptr, "GID %lu", &rec_id) == 1) { /* Try a GID record. */
717                 map->xid.id = rec_id;
718                 map->xid.type = ID_TYPE_GID;
719                 DEBUG(10,("Found gid record %s -> %s \n", keystr, (const char *)data.dptr ));
720                 ret = NT_STATUS_OK;
721
722         } else { /* Unknown record type ! */
723                 DEBUG(2, ("Found INVALID record %s -> %s\n", keystr, (const char *)data.dptr));
724                 ret = NT_STATUS_INTERNAL_DB_ERROR;
725                 goto done;
726         }
727
728         /* apply filters before returning result */
729         if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
730                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
731                                 map->xid.id, dom->low_id, dom->high_id));
732                 ret = NT_STATUS_NONE_MAPPED;
733         }
734
735 done:
736         talloc_free(tmp_ctx);
737         return ret;
738 }
739
740 /*
741   lookup a set of unix ids. 
742 */
743 static NTSTATUS idmap_tdb2_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
744 {
745         NTSTATUS ret;
746         int i;
747
748         /* initialize the status to avoid suprise */
749         for (i = 0; ids[i]; i++) {
750                 ids[i]->status = ID_UNKNOWN;
751         }
752
753         for (i = 0; ids[i]; i++) {
754                 ret = idmap_tdb2_id_to_sid(dom, ids[i]);
755                 if ( ! NT_STATUS_IS_OK(ret)) {
756
757                         /* if it is just a failed mapping continue */
758                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
759
760                                 /* make sure it is marked as unmapped */
761                                 ids[i]->status = ID_UNMAPPED;
762                                 continue;
763                         }
764
765                         /* some fatal error occurred, return immediately */
766                         goto done;
767                 }
768
769                 /* all ok, id is mapped */
770                 ids[i]->status = ID_MAPPED;
771         }
772
773         ret = NT_STATUS_OK;
774
775 done:
776         return ret;
777 }
778
779 /*
780   lookup a set of sids. 
781 */
782
783 struct idmap_tdb2_sids_to_unixids_context {
784         struct idmap_domain *dom;
785         struct id_map **ids;
786         bool allocate_unmapped;
787 };
788
789 static NTSTATUS idmap_tdb2_sids_to_unixids_action(struct db_context *db,
790                                                   void *private_data)
791 {
792         struct idmap_tdb2_sids_to_unixids_context *state;
793         int i;
794         NTSTATUS ret = NT_STATUS_OK;
795
796         state = (struct idmap_tdb2_sids_to_unixids_context *)private_data;
797
798         DEBUG(10, ("idmap_tdb2_sids_to_unixids_action: "
799                    " domain: [%s], allocate: %s\n",
800                    state->dom->name,
801                    state->allocate_unmapped ? "yes" : "no"));
802
803         for (i = 0; state->ids[i]; i++) {
804                 if ((state->ids[i]->status == ID_UNKNOWN) ||
805                     /* retry if we could not map in previous run: */
806                     (state->ids[i]->status == ID_UNMAPPED))
807                 {
808                         NTSTATUS ret2;
809
810                         ret2 = idmap_tdb2_sid_to_id(state->dom, state->ids[i]);
811                         if (!NT_STATUS_IS_OK(ret2)) {
812
813                                 /* if it is just a failed mapping, continue */
814                                 if (NT_STATUS_EQUAL(ret2, NT_STATUS_NONE_MAPPED)) {
815
816                                         /* make sure it is marked as unmapped */
817                                         state->ids[i]->status = ID_UNMAPPED;
818                                         ret = STATUS_SOME_UNMAPPED;
819                                 } else {
820                                         /* some fatal error occurred, return immediately */
821                                         ret = ret2;
822                                         goto done;
823                                 }
824                         } else {
825                                 /* all ok, id is mapped */
826                                 state->ids[i]->status = ID_MAPPED;
827                         }
828                 }
829
830                 if ((state->ids[i]->status == ID_UNMAPPED) &&
831                     state->allocate_unmapped)
832                 {
833                         ret = idmap_tdb2_new_mapping(state->dom, state->ids[i]);
834                         if (!NT_STATUS_IS_OK(ret)) {
835                                 goto done;
836                         }
837                 }
838         }
839
840 done:
841         return ret;
842 }
843
844 static NTSTATUS idmap_tdb2_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
845 {
846         NTSTATUS ret;
847         int i;
848         struct idmap_tdb2_sids_to_unixids_context state;
849         struct idmap_tdb2_context *ctx;
850
851         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
852
853         /* initialize the status to avoid suprise */
854         for (i = 0; ids[i]; i++) {
855                 ids[i]->status = ID_UNKNOWN;
856         }
857
858         state.dom = dom;
859         state.ids = ids;
860         state.allocate_unmapped = false;
861
862         ret = idmap_tdb2_sids_to_unixids_action(ctx->db, &state);
863
864         if (NT_STATUS_EQUAL(ret, STATUS_SOME_UNMAPPED) && !dom->read_only) {
865                 state.allocate_unmapped = true;
866                 ret = dbwrap_trans_do(ctx->db,
867                                       idmap_tdb2_sids_to_unixids_action,
868                                       &state);
869         }
870
871         return ret;
872 }
873
874
875 /*
876   Close the idmap tdb instance
877 */
878 static NTSTATUS idmap_tdb2_close(struct idmap_domain *dom)
879 {
880         /* don't do anything */
881         return NT_STATUS_OK;
882 }
883
884 static struct idmap_methods db_methods = {
885         .init            = idmap_tdb2_db_init,
886         .unixids_to_sids = idmap_tdb2_unixids_to_sids,
887         .sids_to_unixids = idmap_tdb2_sids_to_unixids,
888         .allocate_id     = idmap_tdb2_get_new_id,
889         .close_fn        = idmap_tdb2_close
890 };
891
892 NTSTATUS idmap_tdb2_init(void)
893 {
894         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb2", &db_methods);
895 }