s3:idmap_tdb2: remove the undocumented option of the silly name "tdb:idmap2.tdb"
[ira/wip.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 "system/filesys.h"
36 #include "winbindd.h"
37 #include "idmap.h"
38 #include "idmap_rw.h"
39 #include "dbwrap.h"
40 #include "../libcli/security/dom_sid.h"
41 #include "util_tdb.h"
42
43 #undef DBGC_CLASS
44 #define DBGC_CLASS DBGC_IDMAP
45
46 struct idmap_tdb2_context {
47         struct db_context *db;
48         const char *script; /* script to provide idmaps */
49         struct idmap_rw_ops *rw_ops;
50 };
51
52 /* High water mark keys */
53 #define HWM_GROUP  "GROUP HWM"
54 #define HWM_USER   "USER HWM"
55
56
57 /*
58  * check and initialize high/low water marks in the db
59  */
60 static NTSTATUS idmap_tdb2_init_hwm(struct idmap_domain *dom)
61 {
62         NTSTATUS status;
63         uint32 low_id;
64         struct idmap_tdb2_context *ctx;
65
66         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
67
68         /* Create high water marks for group and user id */
69
70         low_id = dbwrap_fetch_int32(ctx->db, HWM_USER);
71         if ((low_id == -1) || (low_id < dom->low_id)) {
72                 status = dbwrap_trans_store_int32(ctx->db, HWM_USER,
73                                                   dom->low_id);
74                 if (!NT_STATUS_IS_OK(status)) {
75                         DEBUG(0, ("Unable to initialise user hwm in idmap "
76                                   "database: %s\n", nt_errstr(status)));
77                         return NT_STATUS_INTERNAL_DB_ERROR;
78                 }
79         }
80
81         low_id = dbwrap_fetch_int32(ctx->db, HWM_GROUP);
82         if ((low_id == -1) || (low_id < dom->low_id)) {
83                 status = dbwrap_trans_store_int32(ctx->db, HWM_GROUP,
84                                                   dom->low_id);
85                 if (!NT_STATUS_IS_OK(status)) {
86                         DEBUG(0, ("Unable to initialise group hwm in idmap "
87                                   "database: %s\n", nt_errstr(status)));
88                         return NT_STATUS_INTERNAL_DB_ERROR;
89                 }
90         }
91
92         return NT_STATUS_OK;
93 }
94
95
96 /*
97   open the permanent tdb
98  */
99 static NTSTATUS idmap_tdb2_open_db(struct idmap_domain *dom)
100 {
101         char *db_path;
102         struct idmap_tdb2_context *ctx;
103
104         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
105
106         if (ctx->db) {
107                 /* its already open */
108                 return NT_STATUS_OK;
109         }
110
111         db_path = talloc_asprintf(NULL, "%s/idmap2.tdb", lp_private_dir());
112         NT_STATUS_HAVE_NO_MEMORY(db_path);
113
114         /* Open idmap repository */
115         ctx->db = db_open(ctx, db_path, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0644);
116         TALLOC_FREE(db_path);
117
118         if (ctx->db == NULL) {
119                 DEBUG(0, ("Unable to open idmap_tdb2 database '%s'\n",
120                           db_path));
121                 return NT_STATUS_UNSUCCESSFUL;
122         }
123
124         return idmap_tdb2_init_hwm(dom);
125 }
126
127
128 /*
129   Allocate a new id. 
130 */
131
132 struct idmap_tdb2_allocate_id_context {
133         const char *hwmkey;
134         const char *hwmtype;
135         uint32_t high_hwm;
136         uint32_t hwm;
137 };
138
139 static NTSTATUS idmap_tdb2_allocate_id_action(struct db_context *db,
140                                               void *private_data)
141 {
142         NTSTATUS ret;
143         struct idmap_tdb2_allocate_id_context *state;
144         uint32_t hwm;
145
146         state = (struct idmap_tdb2_allocate_id_context *)private_data;
147
148         hwm = dbwrap_fetch_int32(db, state->hwmkey);
149         if (hwm == -1) {
150                 ret = NT_STATUS_INTERNAL_DB_ERROR;
151                 goto done;
152         }
153
154         /* check it is in the range */
155         if (hwm > state->high_hwm) {
156                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
157                           state->hwmtype, (unsigned long)state->high_hwm));
158                 ret = NT_STATUS_UNSUCCESSFUL;
159                 goto done;
160         }
161
162         /* fetch a new id and increment it */
163         ret = dbwrap_trans_change_uint32_atomic(db, state->hwmkey, &hwm, 1);
164         if (!NT_STATUS_IS_OK(ret)) {
165                 DEBUG(1, ("Fatal error while fetching a new %s value\n!",
166                           state->hwmtype));
167                 goto done;
168         }
169
170         /* recheck it is in the range */
171         if (hwm > state->high_hwm) {
172                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
173                           state->hwmtype, (unsigned long)state->high_hwm));
174                 ret = NT_STATUS_UNSUCCESSFUL;
175                 goto done;
176         }
177
178         ret = NT_STATUS_OK;
179         state->hwm = hwm;
180
181 done:
182         return ret;
183 }
184
185 static NTSTATUS idmap_tdb2_allocate_id(struct idmap_domain *dom,
186                                        struct unixid *xid)
187 {
188         const char *hwmkey;
189         const char *hwmtype;
190         uint32_t high_hwm;
191         uint32_t hwm = 0;
192         NTSTATUS status;
193         struct idmap_tdb2_allocate_id_context state;
194         struct idmap_tdb2_context *ctx;
195
196         status = idmap_tdb2_open_db(dom);
197         NT_STATUS_NOT_OK_RETURN(status);
198
199         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
200
201         /* Get current high water mark */
202         switch (xid->type) {
203
204         case ID_TYPE_UID:
205                 hwmkey = HWM_USER;
206                 hwmtype = "UID";
207                 break;
208
209         case ID_TYPE_GID:
210                 hwmkey = HWM_GROUP;
211                 hwmtype = "GID";
212                 break;
213
214         default:
215                 DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type));
216                 return NT_STATUS_INVALID_PARAMETER;
217         }
218
219         high_hwm = dom->high_id;
220
221         state.hwm = hwm;
222         state.high_hwm = high_hwm;
223         state.hwmtype = hwmtype;
224         state.hwmkey = hwmkey;
225
226         status = dbwrap_trans_do(ctx->db, idmap_tdb2_allocate_id_action,
227                                  &state);
228
229         if (NT_STATUS_IS_OK(status)) {
230                 xid->id = state.hwm;
231                 DEBUG(10,("New %s = %d\n", hwmtype, state.hwm));
232         } else {
233                 DEBUG(1, ("Error allocating a new %s\n", hwmtype));
234         }
235
236         return status;
237 }
238
239 /**
240  * Allocate a new unix-ID.
241  * For now this is for the default idmap domain only.
242  * Should be extended later on.
243  */
244 static NTSTATUS idmap_tdb2_get_new_id(struct idmap_domain *dom,
245                                       struct unixid *id)
246 {
247         NTSTATUS ret;
248
249         if (!strequal(dom->name, "*")) {
250                 DEBUG(3, ("idmap_tdb2_get_new_id: "
251                           "Refusing creation of mapping for domain'%s'. "
252                           "Currently only supported for the default "
253                           "domain \"*\".\n",
254                            dom->name));
255                 return NT_STATUS_NOT_IMPLEMENTED;
256         }
257
258         ret = idmap_tdb2_allocate_id(dom, id);
259
260         return ret;
261 }
262
263 /*
264   IDMAP MAPPING TDB BACKEND
265 */
266
267 static NTSTATUS idmap_tdb2_set_mapping(struct idmap_domain *dom,
268                                        const struct id_map *map);
269
270 /*
271   Initialise idmap database. 
272 */
273 static NTSTATUS idmap_tdb2_db_init(struct idmap_domain *dom)
274 {
275         NTSTATUS ret;
276         struct idmap_tdb2_context *ctx;
277         char *config_option = NULL;
278         const char * idmap_script = NULL;
279
280         ctx = talloc_zero(dom, struct idmap_tdb2_context);
281         if ( ! ctx) {
282                 DEBUG(0, ("Out of memory!\n"));
283                 return NT_STATUS_NO_MEMORY;
284         }
285
286         config_option = talloc_asprintf(ctx, "idmap config %s", dom->name);
287         if (config_option == NULL) {
288                 DEBUG(0, ("Out of memory!\n"));
289                 ret = NT_STATUS_NO_MEMORY;
290                 goto failed;
291         }
292         ctx->script = lp_parm_const_string(-1, config_option, "script", "NULL");
293         talloc_free(config_option);
294
295         idmap_script = lp_parm_const_string(-1, "idmap", "script", NULL);
296         if (idmap_script != NULL) {
297                 DEBUG(0, ("Warning: 'idmap:script' is deprecated. "
298                           " Please use 'idmap config * : script' instead!\n"));
299         }
300
301         if (strequal(dom->name, "*") && ctx->script == NULL) {
302                 /* fall back to idmap:script for backwards compatibility */
303                 ctx->script = idmap_script;
304         }
305
306         if (ctx->script) {
307                 DEBUG(1, ("using idmap script '%s'\n", ctx->script));
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         if (keystr == NULL) {
579                 DEBUG(0, ("Out of memory!\n"));
580                 ret = NT_STATUS_NO_MEMORY;
581                 goto done;
582         }
583
584         DEBUG(10,("Fetching record %s\n", keystr));
585
586         /* Check if the mapping exists */
587         data = dbwrap_fetch_bystring(ctx->db, keystr, keystr);
588
589         if (!data.dptr) {
590                 char *sidstr;
591                 struct idmap_tdb2_set_mapping_context store_state;
592
593                 DEBUG(10,("Record %s not found\n", keystr));
594                 if (ctx->script == NULL) {
595                         ret = NT_STATUS_NONE_MAPPED;
596                         goto done;
597                 }
598
599                 ret = idmap_tdb2_script(ctx, map, "IDTOSID %s", keystr);
600                 if (!NT_STATUS_IS_OK(ret)) {
601                         goto done;
602                 }
603
604                 sidstr = sid_string_talloc(keystr, map->sid);
605                 if (!sidstr) {
606                         ret = NT_STATUS_NO_MEMORY;
607                         goto done;
608                 }
609
610                 store_state.ksidstr = sidstr;
611                 store_state.kidstr = keystr;
612
613                 ret = dbwrap_trans_do(ctx->db, idmap_tdb2_set_mapping_action,
614                                       &store_state);
615                 goto done;
616         }
617
618         if (!string_to_sid(map->sid, (const char *)data.dptr)) {
619                 DEBUG(10,("INVALID SID (%s) in record %s\n",
620                         (const char *)data.dptr, keystr));
621                 ret = NT_STATUS_INTERNAL_DB_ERROR;
622                 goto done;
623         }
624
625         DEBUG(10,("Found record %s -> %s\n", keystr, (const char *)data.dptr));
626         ret = NT_STATUS_OK;
627
628 done:
629         talloc_free(keystr);
630         return ret;
631 }
632
633
634 /*
635  Single sid to id lookup function. 
636 */
637 static NTSTATUS idmap_tdb2_sid_to_id(struct idmap_domain *dom, struct id_map *map)
638 {
639         NTSTATUS ret;
640         TDB_DATA data;
641         char *keystr;
642         unsigned long rec_id = 0;
643         struct idmap_tdb2_context *ctx;
644         TALLOC_CTX *tmp_ctx = talloc_stackframe();
645
646         ret = idmap_tdb2_open_db(dom);
647         NT_STATUS_NOT_OK_RETURN(ret);
648
649         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
650
651         keystr = sid_string_talloc(tmp_ctx, map->sid);
652         if (keystr == NULL) {
653                 DEBUG(0, ("Out of memory!\n"));
654                 ret = NT_STATUS_NO_MEMORY;
655                 goto done;
656         }
657
658         DEBUG(10,("Fetching record %s\n", keystr));
659
660         /* Check if sid is present in database */
661         data = dbwrap_fetch_bystring(ctx->db, tmp_ctx, keystr);
662         if (!data.dptr) {
663                 char *idstr;
664                 struct idmap_tdb2_set_mapping_context store_state;
665
666                 DEBUG(10,(__location__ " Record %s not found\n", keystr));
667
668                 if (ctx->script == NULL) {
669                         ret = NT_STATUS_NONE_MAPPED;
670                         goto done;
671                 }
672
673                 ret = idmap_tdb2_script(ctx, map, "SIDTOID %s", keystr);
674                 if (!NT_STATUS_IS_OK(ret)) {
675                         goto done;
676                 }
677
678                 /* apply filters before returning result */
679                 if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
680                         DEBUG(5, ("Script returned id (%u) out of range "
681                                   "(%u - %u). Filtered!\n",
682                                   map->xid.id, dom->low_id, dom->high_id));
683                         ret = NT_STATUS_NONE_MAPPED;
684                         goto done;
685                 }
686
687                 idstr = talloc_asprintf(tmp_ctx, "%cID %lu",
688                                         map->xid.type == ID_TYPE_UID?'U':'G',
689                                         (unsigned long)map->xid.id);
690                 if (idstr == NULL) {
691                         ret = NT_STATUS_NO_MEMORY;
692                         goto done;
693                 }
694
695                 store_state.ksidstr = keystr;
696                 store_state.kidstr = idstr;
697
698                 ret = dbwrap_trans_do(ctx->db, idmap_tdb2_set_mapping_action,
699                                       &store_state);
700                 goto done;
701         }
702
703         /* What type of record is this ? */
704         if (sscanf((const char *)data.dptr, "UID %lu", &rec_id) == 1) { /* Try a UID record. */
705                 map->xid.id = rec_id;
706                 map->xid.type = ID_TYPE_UID;
707                 DEBUG(10,("Found uid record %s -> %s \n", keystr, (const char *)data.dptr ));
708                 ret = NT_STATUS_OK;
709
710         } else if (sscanf((const char *)data.dptr, "GID %lu", &rec_id) == 1) { /* Try a GID record. */
711                 map->xid.id = rec_id;
712                 map->xid.type = ID_TYPE_GID;
713                 DEBUG(10,("Found gid record %s -> %s \n", keystr, (const char *)data.dptr ));
714                 ret = NT_STATUS_OK;
715
716         } else { /* Unknown record type ! */
717                 DEBUG(2, ("Found INVALID record %s -> %s\n", keystr, (const char *)data.dptr));
718                 ret = NT_STATUS_INTERNAL_DB_ERROR;
719                 goto done;
720         }
721
722         /* apply filters before returning result */
723         if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
724                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
725                                 map->xid.id, dom->low_id, dom->high_id));
726                 ret = NT_STATUS_NONE_MAPPED;
727         }
728
729 done:
730         talloc_free(tmp_ctx);
731         return ret;
732 }
733
734 /*
735   lookup a set of unix ids. 
736 */
737 static NTSTATUS idmap_tdb2_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
738 {
739         NTSTATUS ret;
740         int i;
741
742         /* initialize the status to avoid suprise */
743         for (i = 0; ids[i]; i++) {
744                 ids[i]->status = ID_UNKNOWN;
745         }
746
747         for (i = 0; ids[i]; i++) {
748                 ret = idmap_tdb2_id_to_sid(dom, ids[i]);
749                 if ( ! NT_STATUS_IS_OK(ret)) {
750
751                         /* if it is just a failed mapping continue */
752                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
753
754                                 /* make sure it is marked as unmapped */
755                                 ids[i]->status = ID_UNMAPPED;
756                                 continue;
757                         }
758
759                         /* some fatal error occurred, return immediately */
760                         goto done;
761                 }
762
763                 /* all ok, id is mapped */
764                 ids[i]->status = ID_MAPPED;
765         }
766
767         ret = NT_STATUS_OK;
768
769 done:
770         return ret;
771 }
772
773 /*
774   lookup a set of sids. 
775 */
776
777 struct idmap_tdb2_sids_to_unixids_context {
778         struct idmap_domain *dom;
779         struct id_map **ids;
780         bool allocate_unmapped;
781 };
782
783 static NTSTATUS idmap_tdb2_sids_to_unixids_action(struct db_context *db,
784                                                   void *private_data)
785 {
786         struct idmap_tdb2_sids_to_unixids_context *state;
787         int i;
788         NTSTATUS ret = NT_STATUS_OK;
789
790         state = (struct idmap_tdb2_sids_to_unixids_context *)private_data;
791
792         DEBUG(10, ("idmap_tdb2_sids_to_unixids_action: "
793                    " domain: [%s], allocate: %s\n",
794                    state->dom->name,
795                    state->allocate_unmapped ? "yes" : "no"));
796
797         for (i = 0; state->ids[i]; i++) {
798                 if ((state->ids[i]->status == ID_UNKNOWN) ||
799                     /* retry if we could not map in previous run: */
800                     (state->ids[i]->status == ID_UNMAPPED))
801                 {
802                         NTSTATUS ret2;
803
804                         ret2 = idmap_tdb2_sid_to_id(state->dom, state->ids[i]);
805                         if (!NT_STATUS_IS_OK(ret2)) {
806
807                                 /* if it is just a failed mapping, continue */
808                                 if (NT_STATUS_EQUAL(ret2, NT_STATUS_NONE_MAPPED)) {
809
810                                         /* make sure it is marked as unmapped */
811                                         state->ids[i]->status = ID_UNMAPPED;
812                                         ret = STATUS_SOME_UNMAPPED;
813                                 } else {
814                                         /* some fatal error occurred, return immediately */
815                                         ret = ret2;
816                                         goto done;
817                                 }
818                         } else {
819                                 /* all ok, id is mapped */
820                                 state->ids[i]->status = ID_MAPPED;
821                         }
822                 }
823
824                 if ((state->ids[i]->status == ID_UNMAPPED) &&
825                     state->allocate_unmapped)
826                 {
827                         ret = idmap_tdb2_new_mapping(state->dom, state->ids[i]);
828                         if (!NT_STATUS_IS_OK(ret)) {
829                                 goto done;
830                         }
831                 }
832         }
833
834 done:
835         return ret;
836 }
837
838 static NTSTATUS idmap_tdb2_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
839 {
840         NTSTATUS ret;
841         int i;
842         struct idmap_tdb2_sids_to_unixids_context state;
843         struct idmap_tdb2_context *ctx;
844
845         ctx = talloc_get_type(dom->private_data, struct idmap_tdb2_context);
846
847         /* initialize the status to avoid suprise */
848         for (i = 0; ids[i]; i++) {
849                 ids[i]->status = ID_UNKNOWN;
850         }
851
852         state.dom = dom;
853         state.ids = ids;
854         state.allocate_unmapped = false;
855
856         ret = idmap_tdb2_sids_to_unixids_action(ctx->db, &state);
857
858         if (NT_STATUS_EQUAL(ret, STATUS_SOME_UNMAPPED) && !dom->read_only) {
859                 state.allocate_unmapped = true;
860                 ret = dbwrap_trans_do(ctx->db,
861                                       idmap_tdb2_sids_to_unixids_action,
862                                       &state);
863         }
864
865         return ret;
866 }
867
868
869 static struct idmap_methods db_methods = {
870         .init            = idmap_tdb2_db_init,
871         .unixids_to_sids = idmap_tdb2_unixids_to_sids,
872         .sids_to_unixids = idmap_tdb2_sids_to_unixids,
873         .allocate_id     = idmap_tdb2_get_new_id
874 };
875
876 NTSTATUS idmap_tdb2_init(void)
877 {
878         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb2", &db_methods);
879 }