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