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