s3:idmap: convert idmap_tdb to use dbwrap wrapper functions.
[samba.git] / source3 / winbindd / idmap_tdb.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    idmap TDB backend
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
8    Copyright (C) Jeremy Allison 2006
9    Copyright (C) Simo Sorce 2003-2006
10    Copyright (C) Michael Adam 2009-2010
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "system/filesys.h"
28 #include "winbindd.h"
29 #include "idmap.h"
30 #include "idmap_rw.h"
31 #include "dbwrap/dbwrap.h"
32 #include "dbwrap/dbwrap_open.h"
33 #include "../libcli/security/security.h"
34 #include "util_tdb.h"
35
36 #undef DBGC_CLASS
37 #define DBGC_CLASS DBGC_IDMAP
38
39 /* idmap version determines auto-conversion - this is the database
40    structure version specifier. */
41
42 #define IDMAP_VERSION 2
43
44 struct idmap_tdb_context {
45         struct db_context *db;
46         struct idmap_rw_ops *rw_ops;
47 };
48
49 /* High water mark keys */
50 #define HWM_GROUP  "GROUP HWM"
51 #define HWM_USER   "USER HWM"
52
53 struct convert_fn_state {
54         struct db_context *db;
55         bool failed;
56 };
57
58 /*****************************************************************************
59  For idmap conversion: convert one record to new format
60  Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid
61  instead of the SID.
62 *****************************************************************************/
63 static int convert_fn(struct db_record *rec, void *private_data)
64 {
65         struct winbindd_domain *domain;
66         char *p;
67         NTSTATUS status;
68         struct dom_sid sid;
69         uint32 rid;
70         fstring keystr;
71         fstring dom_name;
72         TDB_DATA key;
73         TDB_DATA key2;
74         TDB_DATA value;
75         struct convert_fn_state *s = (struct convert_fn_state *)private_data;
76
77         key = dbwrap_record_get_key(rec);
78
79         DEBUG(10,("Converting %s\n", (const char *)key.dptr));
80
81         p = strchr((const char *)key.dptr, '/');
82         if (!p)
83                 return 0;
84
85         *p = 0;
86         fstrcpy(dom_name, (const char *)key.dptr);
87         *p++ = '/';
88
89         domain = find_domain_from_name(dom_name);
90         if (domain == NULL) {
91                 /* We must delete the old record. */
92                 DEBUG(0,("Unable to find domain %s\n", dom_name ));
93                 DEBUG(0,("deleting record %s\n", (const char *)key.dptr ));
94
95                 status = dbwrap_record_delete(rec);
96                 if (!NT_STATUS_IS_OK(status)) {
97                         DEBUG(0, ("Unable to delete record %s:%s\n",
98                                 (const char *)key.dptr,
99                                 nt_errstr(status)));
100                         s->failed = true;
101                         return -1;
102                 }
103
104                 return 0;
105         }
106
107         rid = atoi(p);
108
109         sid_compose(&sid, &domain->sid, rid);
110
111         sid_to_fstring(keystr, &sid);
112         key2 = string_term_tdb_data(keystr);
113
114         value = dbwrap_record_get_value(rec);
115
116         status = dbwrap_store(s->db, key2, value, TDB_INSERT);
117         if (!NT_STATUS_IS_OK(status)) {
118                 DEBUG(0,("Unable to add record %s:%s\n",
119                         (const char *)key2.dptr,
120                         nt_errstr(status)));
121                 s->failed = true;
122                 return -1;
123         }
124
125         status = dbwrap_store(s->db, value, key2, TDB_REPLACE);
126         if (!NT_STATUS_IS_OK(status)) {
127                 DEBUG(0,("Unable to update record %s:%s\n",
128                         (const char *)value.dptr,
129                         nt_errstr(status)));
130                 s->failed = true;
131                 return -1;
132         }
133
134         status = dbwrap_record_delete(rec);
135         if (!NT_STATUS_IS_OK(status)) {
136                 DEBUG(0,("Unable to delete record %s:%s\n",
137                         (const char *)key.dptr,
138                         nt_errstr(status)));
139                 s->failed = true;
140                 return -1;
141         }
142
143         return 0;
144 }
145
146 /*****************************************************************************
147  Convert the idmap database from an older version.
148 *****************************************************************************/
149
150 static bool idmap_tdb_upgrade(struct idmap_domain *dom, struct db_context *db)
151 {
152         int32 vers;
153         bool bigendianheader;
154         struct convert_fn_state s;
155         NTSTATUS status;
156
157 #if BUILD_TDB2
158         /* If we are bigendian, tdb is bigendian if NOT converted. */
159         union {
160                 uint16 large;
161                 unsigned char small[2];
162         } u;
163         u.large = 0x0102;
164         if (u.small[0] == 0x01)
165                 bigendianheader = !(dbwrap_get_flags(db) & TDB_CONVERT);
166         else {
167                 assert(u.small[0] == 0x02);
168                 bigendianheader = (dbwrap_get_flags(db) & TDB_CONVERT);
169         }
170 #else
171         bigendianheader = (dbwrap_get_flags(db) & TDB_BIGENDIAN) ? True : False;
172 #endif
173         DEBUG(0, ("Upgrading winbindd_idmap.tdb from an old version\n"));
174
175         vers = dbwrap_fetch_int32(db, "IDMAP_VERSION");
176
177         if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) {
178                 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
179                 /*
180                  * high and low records were created on a
181                  * big endian machine and will need byte-reversing.
182                  */
183
184                 int32 wm;
185
186                 wm = dbwrap_fetch_int32(db, HWM_USER);
187
188                 if (wm != -1) {
189                         wm = IREV(wm);
190                 }  else {
191                         wm = dom->low_id;
192                 }
193
194                 if (dbwrap_store_int32(db, HWM_USER, wm) == -1) {
195                         DEBUG(0, ("Unable to byteswap user hwm in idmap database\n"));
196                         return False;
197                 }
198
199                 wm = dbwrap_fetch_int32(db, HWM_GROUP);
200                 if (wm != -1) {
201                         wm = IREV(wm);
202                 } else {
203                         wm = dom->low_id;
204                 }
205
206                 if (dbwrap_store_int32(db, HWM_GROUP, wm) == -1) {
207                         DEBUG(0, ("Unable to byteswap group hwm in idmap database\n"));
208                         return False;
209                 }
210         }
211
212         s.db = db;
213         s.failed = false;
214
215         /* the old format stored as DOMAIN/rid - now we store the SID direct */
216         status = dbwrap_traverse(db, convert_fn, &s, NULL);
217
218         if (!NT_STATUS_IS_OK(status)) {
219                 DEBUG(0, ("Database traverse failed during conversion\n"));
220                 return false;
221         }
222
223         if (s.failed) {
224                 DEBUG(0, ("Problem during conversion\n"));
225                 return False;
226         }
227
228         if (dbwrap_store_int32(db, "IDMAP_VERSION", IDMAP_VERSION) == -1) {
229                 DEBUG(0, ("Unable to store idmap version in database\n"));
230                 return False;
231         }
232
233         return True;
234 }
235
236 static NTSTATUS idmap_tdb_init_hwm(struct idmap_domain *dom)
237 {
238         int ret;
239         uint32_t low_uid;
240         uint32_t low_gid;
241         bool update_uid = false;
242         bool update_gid = false;
243         struct idmap_tdb_context *ctx;
244
245         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
246
247         low_uid = dbwrap_fetch_int32(ctx->db, HWM_USER);
248         if (low_uid == -1 || low_uid < dom->low_id) {
249                 update_uid = true;
250         }
251
252         low_gid = dbwrap_fetch_int32(ctx->db, HWM_GROUP);
253         if (low_gid == -1 || low_gid < dom->low_id) {
254                 update_gid = true;
255         }
256
257         if (!update_uid && !update_gid) {
258                 return NT_STATUS_OK;
259         }
260
261         if (dbwrap_transaction_start(ctx->db) != 0) {
262                 DEBUG(0, ("Unable to start upgrade transaction!\n"));
263                 return NT_STATUS_INTERNAL_DB_ERROR;
264         }
265
266         if (update_uid) {
267                 ret = dbwrap_store_int32(ctx->db, HWM_USER, dom->low_id);
268                 if (ret == -1) {
269                         dbwrap_transaction_cancel(ctx->db);
270                         DEBUG(0, ("Unable to initialise user hwm in idmap "
271                                   "database\n"));
272                         return NT_STATUS_INTERNAL_DB_ERROR;
273                 }
274         }
275
276         if (update_gid) {
277                 ret = dbwrap_store_int32(ctx->db, HWM_GROUP, dom->low_id);
278                 if (ret == -1) {
279                         dbwrap_transaction_cancel(ctx->db);
280                         DEBUG(0, ("Unable to initialise group hwm in idmap "
281                                   "database\n"));
282                         return NT_STATUS_INTERNAL_DB_ERROR;
283                 }
284         }
285
286         if (dbwrap_transaction_commit(ctx->db) != 0) {
287                 DEBUG(0, ("Unable to commit upgrade transaction!\n"));
288                 return NT_STATUS_INTERNAL_DB_ERROR;
289         }
290
291         return NT_STATUS_OK;
292 }
293
294 static NTSTATUS idmap_tdb_open_db(struct idmap_domain *dom)
295 {
296         NTSTATUS ret;
297         TALLOC_CTX *mem_ctx;
298         char *tdbfile = NULL;
299         struct db_context *db = NULL;
300         int32_t version;
301         bool config_error = false;
302         struct idmap_tdb_context *ctx;
303
304         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
305
306         if (ctx->db) {
307                 /* it is already open */
308                 return NT_STATUS_OK;
309         }
310
311         /* use our own context here */
312         mem_ctx = talloc_stackframe();
313
314         /* use the old database if present */
315         tdbfile = state_path("winbindd_idmap.tdb");
316         if (!tdbfile) {
317                 DEBUG(0, ("Out of memory!\n"));
318                 ret = NT_STATUS_NO_MEMORY;
319                 goto done;
320         }
321
322         DEBUG(10,("Opening tdbfile %s\n", tdbfile ));
323
324         /* Open idmap repository */
325         db = db_open(mem_ctx, tdbfile, 0, TDB_DEFAULT, O_RDWR | O_CREAT, 0644);
326         if (!db) {
327                 DEBUG(0, ("Unable to open idmap database\n"));
328                 ret = NT_STATUS_UNSUCCESSFUL;
329                 goto done;
330         }
331
332         /* check against earlier versions */
333         version = dbwrap_fetch_int32(db, "IDMAP_VERSION");
334         if (version != IDMAP_VERSION) {
335                 if (config_error) {
336                         DEBUG(0,("Upgrade of IDMAP_VERSION from %d to %d is not "
337                                  "possible with incomplete configuration\n",
338                                  version, IDMAP_VERSION));
339                         ret = NT_STATUS_UNSUCCESSFUL;
340                         goto done;
341                 }
342                 if (dbwrap_transaction_start(db) != 0) {
343                         DEBUG(0, ("Unable to start upgrade transaction!\n"));
344                         ret = NT_STATUS_INTERNAL_DB_ERROR;
345                         goto done;
346                 }
347
348                 if (!idmap_tdb_upgrade(dom, db)) {
349                         dbwrap_transaction_cancel(db);
350                         DEBUG(0, ("Unable to open idmap database, it's in an old format, and upgrade failed!\n"));
351                         ret = NT_STATUS_INTERNAL_DB_ERROR;
352                         goto done;
353                 }
354
355                 if (dbwrap_transaction_commit(db) != 0) {
356                         DEBUG(0, ("Unable to commit upgrade transaction!\n"));
357                         ret = NT_STATUS_INTERNAL_DB_ERROR;
358                         goto done;
359                 }
360         }
361
362         ctx->db = talloc_move(ctx, &db);
363
364         ret = idmap_tdb_init_hwm(dom);
365
366 done:
367         talloc_free(mem_ctx);
368         return ret;
369 }
370
371 /**********************************************************************
372  IDMAP ALLOC TDB BACKEND
373 **********************************************************************/
374
375 /**********************************
376  Allocate a new id. 
377 **********************************/
378
379 struct idmap_tdb_allocate_id_context {
380         const char *hwmkey;
381         const char *hwmtype;
382         uint32_t high_hwm;
383         uint32_t hwm;
384 };
385
386 static NTSTATUS idmap_tdb_allocate_id_action(struct db_context *db,
387                                              void *private_data)
388 {
389         NTSTATUS ret;
390         struct idmap_tdb_allocate_id_context *state;
391         uint32_t hwm;
392
393         state = (struct idmap_tdb_allocate_id_context *)private_data;
394
395         hwm = dbwrap_fetch_int32(db, state->hwmkey);
396         if (hwm == -1) {
397                 ret = NT_STATUS_INTERNAL_DB_ERROR;
398                 goto done;
399         }
400
401         /* check it is in the range */
402         if (hwm > state->high_hwm) {
403                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
404                           state->hwmtype, (unsigned long)state->high_hwm));
405                 ret = NT_STATUS_UNSUCCESSFUL;
406                 goto done;
407         }
408
409         /* fetch a new id and increment it */
410         ret = dbwrap_trans_change_uint32_atomic(db, state->hwmkey, &hwm, 1);
411         if (!NT_STATUS_IS_OK(ret)) {
412                 DEBUG(0, ("Fatal error while fetching a new %s value: %s\n!",
413                           state->hwmtype, nt_errstr(ret)));
414                 goto done;
415         }
416
417         /* recheck it is in the range */
418         if (hwm > state->high_hwm) {
419                 DEBUG(1, ("Fatal Error: %s range full!! (max: %lu)\n",
420                           state->hwmtype, (unsigned long)state->high_hwm));
421                 ret = NT_STATUS_UNSUCCESSFUL;
422                 goto done;
423         }
424
425         ret = NT_STATUS_OK;
426         state->hwm = hwm;
427
428 done:
429         return ret;
430 }
431
432 static NTSTATUS idmap_tdb_allocate_id(struct idmap_domain *dom,
433                                       struct unixid *xid)
434 {
435         const char *hwmkey;
436         const char *hwmtype;
437         uint32_t high_hwm;
438         uint32_t hwm = 0;
439         NTSTATUS status;
440         struct idmap_tdb_allocate_id_context state;
441         struct idmap_tdb_context *ctx;
442
443         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
444
445         /* Get current high water mark */
446         switch (xid->type) {
447
448         case ID_TYPE_UID:
449                 hwmkey = HWM_USER;
450                 hwmtype = "UID";
451                 break;
452
453         case ID_TYPE_GID:
454                 hwmkey = HWM_GROUP;
455                 hwmtype = "GID";
456                 break;
457
458         default:
459                 DEBUG(2, ("Invalid ID type (0x%x)\n", xid->type));
460                 return NT_STATUS_INVALID_PARAMETER;
461         }
462
463         high_hwm = dom->high_id;
464
465         state.hwm = hwm;
466         state.high_hwm = high_hwm;
467         state.hwmtype = hwmtype;
468         state.hwmkey = hwmkey;
469
470         status = dbwrap_trans_do(ctx->db, idmap_tdb_allocate_id_action,
471                                  &state);
472
473         if (NT_STATUS_IS_OK(status)) {
474                 xid->id = state.hwm;
475                 DEBUG(10,("New %s = %d\n", hwmtype, state.hwm));
476         } else {
477                 DEBUG(1, ("Error allocating a new %s\n", hwmtype));
478         }
479
480         return status;
481 }
482
483 /**
484  * Allocate a new unix-ID.
485  * For now this is for the default idmap domain only.
486  * Should be extended later on.
487  */
488 static NTSTATUS idmap_tdb_get_new_id(struct idmap_domain *dom,
489                                      struct unixid *id)
490 {
491         NTSTATUS ret;
492
493         if (!strequal(dom->name, "*")) {
494                 DEBUG(3, ("idmap_tdb_get_new_id: "
495                           "Refusing allocation of a new unixid for domain'%s'. "
496                           "Currently only supported for the default "
497                           "domain \"*\".\n",
498                            dom->name));
499                 return NT_STATUS_NOT_IMPLEMENTED;
500         }
501
502         ret = idmap_tdb_allocate_id(dom, id);
503
504         return ret;
505 }
506
507 /**********************************************************************
508  IDMAP MAPPING TDB BACKEND
509 **********************************************************************/
510
511 /*****************************
512  Initialise idmap database. 
513 *****************************/
514
515 static NTSTATUS idmap_tdb_set_mapping(struct idmap_domain *dom,
516                                       const struct id_map *map);
517
518 static NTSTATUS idmap_tdb_db_init(struct idmap_domain *dom)
519 {
520         NTSTATUS ret;
521         struct idmap_tdb_context *ctx;
522
523         DEBUG(10, ("idmap_tdb_db_init called for domain '%s'\n", dom->name));
524
525         ctx = talloc_zero(dom, struct idmap_tdb_context);
526         if ( ! ctx) {
527                 DEBUG(0, ("Out of memory!\n"));
528                 return NT_STATUS_NO_MEMORY;
529         }
530
531         /* load backend specific configuration here: */
532 #if 0
533         if (strequal(dom->name, "*")) {
534         } else {
535         }
536 #endif
537
538         ctx->rw_ops = talloc_zero(ctx, struct idmap_rw_ops);
539         if (ctx->rw_ops == NULL) {
540                 DEBUG(0, ("Out of memory!\n"));
541                 ret = NT_STATUS_NO_MEMORY;
542                 goto failed;
543         }
544
545         ctx->rw_ops->get_new_id = idmap_tdb_get_new_id;
546         ctx->rw_ops->set_mapping = idmap_tdb_set_mapping;
547
548         dom->private_data = ctx;
549
550         ret = idmap_tdb_open_db(dom);
551         if ( ! NT_STATUS_IS_OK(ret)) {
552                 goto failed;
553         }
554
555         return NT_STATUS_OK;
556
557 failed:
558         talloc_free(ctx);
559         return ret;
560 }
561
562
563 /**
564  * store a mapping in the database
565  */
566
567 struct idmap_tdb_set_mapping_context {
568         const char *ksidstr;
569         const char *kidstr;
570 };
571
572 static NTSTATUS idmap_tdb_set_mapping_action(struct db_context *db,
573                                              void *private_data)
574 {
575         NTSTATUS ret;
576         struct idmap_tdb_set_mapping_context *state;
577
578         state = (struct idmap_tdb_set_mapping_context *)private_data;
579
580         DEBUG(10, ("Storing %s <-> %s map\n", state->ksidstr, state->kidstr));
581
582         ret = dbwrap_store_bystring(db, state->ksidstr,
583                                     string_term_tdb_data(state->kidstr),
584                                     TDB_REPLACE);
585         if (!NT_STATUS_IS_OK(ret)) {
586                 DEBUG(0, ("Error storing SID -> ID (%s -> %s): %s\n",
587                           state->ksidstr, state->kidstr, nt_errstr(ret)));
588                 goto done;
589         }
590
591         ret = dbwrap_store_bystring(db, state->kidstr,
592                                     string_term_tdb_data(state->ksidstr),
593                                     TDB_REPLACE);
594         if (!NT_STATUS_IS_OK(ret)) {
595                 DEBUG(0, ("Error storing ID -> SID (%s -> %s): %s\n",
596                           state->kidstr, state->ksidstr, nt_errstr(ret)));
597                 goto done;
598         }
599
600         DEBUG(10,("Stored %s <-> %s\n", state->ksidstr, state->kidstr));
601         ret = NT_STATUS_OK;
602
603 done:
604         return ret;
605 }
606
607 static NTSTATUS idmap_tdb_set_mapping(struct idmap_domain *dom,
608                                       const struct id_map *map)
609 {
610         struct idmap_tdb_context *ctx;
611         NTSTATUS ret;
612         char *ksidstr, *kidstr;
613         struct idmap_tdb_set_mapping_context state;
614
615         if (!map || !map->sid) {
616                 return NT_STATUS_INVALID_PARAMETER;
617         }
618
619         ksidstr = kidstr = NULL;
620
621         /* TODO: should we filter a set_mapping using low/high filters ? */
622
623         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
624
625         switch (map->xid.type) {
626
627         case ID_TYPE_UID:
628                 kidstr = talloc_asprintf(ctx, "UID %lu",
629                                          (unsigned long)map->xid.id);
630                 break;
631
632         case ID_TYPE_GID:
633                 kidstr = talloc_asprintf(ctx, "GID %lu",
634                                          (unsigned long)map->xid.id);
635                 break;
636
637         default:
638                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
639                 return NT_STATUS_INVALID_PARAMETER;
640         }
641
642         if (kidstr == NULL) {
643                 DEBUG(0, ("ERROR: Out of memory!\n"));
644                 ret = NT_STATUS_NO_MEMORY;
645                 goto done;
646         }
647
648         ksidstr = sid_string_talloc(ctx, map->sid);
649         if (ksidstr == NULL) {
650                 DEBUG(0, ("Out of memory!\n"));
651                 ret = NT_STATUS_NO_MEMORY;
652                 goto done;
653         }
654
655         state.ksidstr = ksidstr;
656         state.kidstr = kidstr;
657
658         ret = dbwrap_trans_do(ctx->db, idmap_tdb_set_mapping_action, &state);
659
660 done:
661         talloc_free(ksidstr);
662         talloc_free(kidstr);
663         return ret;
664 }
665
666 /**
667  * Create a new mapping for an unmapped SID, also allocating a new ID.
668  * This should be run inside a transaction.
669  *
670  * TODO:
671  * Properly integrate this with multi domain idmap config:
672  * Currently, the allocator is default-config only.
673  */
674 static NTSTATUS idmap_tdb_new_mapping(struct idmap_domain *dom, struct id_map *map)
675 {
676         NTSTATUS ret;
677         struct idmap_tdb_context *ctx;
678
679         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
680
681         ret = idmap_rw_new_mapping(dom, ctx->rw_ops, map);
682
683         return ret;
684 }
685
686
687 /**********************************
688  Single id to sid lookup function. 
689 **********************************/
690
691 static NTSTATUS idmap_tdb_id_to_sid(struct idmap_domain *dom, struct id_map *map)
692 {
693         NTSTATUS ret;
694         TDB_DATA data;
695         char *keystr;
696         struct idmap_tdb_context *ctx;
697
698         if (!dom || !map) {
699                 return NT_STATUS_INVALID_PARAMETER;
700         }
701
702         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
703
704         /* apply filters before checking */
705         if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
706                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
707                                 map->xid.id, dom->low_id, dom->high_id));
708                 return NT_STATUS_NONE_MAPPED;
709         }
710
711         switch (map->xid.type) {
712
713         case ID_TYPE_UID:
714                 keystr = talloc_asprintf(ctx, "UID %lu", (unsigned long)map->xid.id);
715                 break;
716
717         case ID_TYPE_GID:
718                 keystr = talloc_asprintf(ctx, "GID %lu", (unsigned long)map->xid.id);
719                 break;
720
721         default:
722                 DEBUG(2, ("INVALID unix ID type: 0x02%x\n", map->xid.type));
723                 return NT_STATUS_INVALID_PARAMETER;
724         }
725
726         /* final SAFE_FREE safe */
727         data.dptr = NULL;
728
729         if (keystr == NULL) {
730                 DEBUG(0, ("Out of memory!\n"));
731                 ret = NT_STATUS_NO_MEMORY;
732                 goto done;
733         }
734
735         DEBUG(10,("Fetching record %s\n", keystr));
736
737         /* Check if the mapping exists */
738         ret = dbwrap_fetch_bystring(ctx->db, NULL, keystr, &data);
739
740         if (!NT_STATUS_IS_OK(ret)) {
741                 DEBUG(10,("Record %s not found\n", keystr));
742                 ret = NT_STATUS_NONE_MAPPED;
743                 goto done;
744         }
745
746         if (!string_to_sid(map->sid, (const char *)data.dptr)) {
747                 DEBUG(10,("INVALID SID (%s) in record %s\n",
748                         (const char *)data.dptr, keystr));
749                 ret = NT_STATUS_INTERNAL_DB_ERROR;
750                 goto done;
751         }
752
753         DEBUG(10,("Found record %s -> %s\n", keystr, (const char *)data.dptr));
754         ret = NT_STATUS_OK;
755
756 done:
757         talloc_free(data.dptr);
758         talloc_free(keystr);
759         return ret;
760 }
761
762 /**********************************
763  Single sid to id lookup function. 
764 **********************************/
765
766 static NTSTATUS idmap_tdb_sid_to_id(struct idmap_domain *dom, struct id_map *map)
767 {
768         NTSTATUS ret;
769         TDB_DATA data;
770         char *keystr;
771         unsigned long rec_id = 0;
772         struct idmap_tdb_context *ctx;
773         TALLOC_CTX *tmp_ctx = talloc_stackframe();
774
775         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
776
777         keystr = sid_string_talloc(tmp_ctx, map->sid);
778         if (keystr == NULL) {
779                 DEBUG(0, ("Out of memory!\n"));
780                 ret = NT_STATUS_NO_MEMORY;
781                 goto done;
782         }
783
784         DEBUG(10,("Fetching record %s\n", keystr));
785
786         /* Check if sid is present in database */
787         ret = dbwrap_fetch_bystring(ctx->db, tmp_ctx, keystr, &data);
788         if (!NT_STATUS_IS_OK(ret)) {
789                 DEBUG(10,("Record %s not found\n", keystr));
790                 ret = NT_STATUS_NONE_MAPPED;
791                 goto done;
792         }
793
794         /* What type of record is this ? */
795         if (sscanf((const char *)data.dptr, "UID %lu", &rec_id) == 1) { /* Try a UID record. */
796                 map->xid.id = rec_id;
797                 map->xid.type = ID_TYPE_UID;
798                 DEBUG(10,("Found uid record %s -> %s \n", keystr, (const char *)data.dptr ));
799                 ret = NT_STATUS_OK;
800
801         } else if (sscanf((const char *)data.dptr, "GID %lu", &rec_id) == 1) { /* Try a GID record. */
802                 map->xid.id = rec_id;
803                 map->xid.type = ID_TYPE_GID;
804                 DEBUG(10,("Found gid record %s -> %s \n", keystr, (const char *)data.dptr ));
805                 ret = NT_STATUS_OK;
806
807         } else { /* Unknown record type ! */
808                 DEBUG(2, ("Found INVALID record %s -> %s\n", keystr, (const char *)data.dptr));
809                 ret = NT_STATUS_INTERNAL_DB_ERROR;
810                 goto done;
811         }
812
813         /* apply filters before returning result */
814         if (!idmap_unix_id_is_in_range(map->xid.id, dom)) {
815                 DEBUG(5, ("Requested id (%u) out of range (%u - %u). Filtered!\n",
816                                 map->xid.id, dom->low_id, dom->high_id));
817                 ret = NT_STATUS_NONE_MAPPED;
818         }
819
820 done:
821         talloc_free(tmp_ctx);
822         return ret;
823 }
824
825 /**********************************
826  lookup a set of unix ids. 
827 **********************************/
828
829 static NTSTATUS idmap_tdb_unixids_to_sids(struct idmap_domain *dom, struct id_map **ids)
830 {
831         NTSTATUS ret;
832         int i;
833
834         /* initialize the status to avoid suprise */
835         for (i = 0; ids[i]; i++) {
836                 ids[i]->status = ID_UNKNOWN;
837         }
838
839         for (i = 0; ids[i]; i++) {
840                 ret = idmap_tdb_id_to_sid(dom, ids[i]);
841                 if ( ! NT_STATUS_IS_OK(ret)) {
842
843                         /* if it is just a failed mapping continue */
844                         if (NT_STATUS_EQUAL(ret, NT_STATUS_NONE_MAPPED)) {
845
846                                 /* make sure it is marked as unmapped */
847                                 ids[i]->status = ID_UNMAPPED;
848                                 continue;
849                         }
850
851                         /* some fatal error occurred, return immediately */
852                         goto done;
853                 }
854
855                 /* all ok, id is mapped */
856                 ids[i]->status = ID_MAPPED;
857         }
858
859         ret = NT_STATUS_OK;
860
861 done:
862         return ret;
863 }
864
865 /**********************************
866  lookup a set of sids. 
867 **********************************/
868
869 struct idmap_tdb_sids_to_unixids_context {
870         struct idmap_domain *dom;
871         struct id_map **ids;
872         bool allocate_unmapped;
873 };
874
875 static NTSTATUS idmap_tdb_sids_to_unixids_action(struct db_context *db,
876                                                  void *private_data)
877 {
878         struct idmap_tdb_sids_to_unixids_context *state;
879         int i;
880         NTSTATUS ret = NT_STATUS_OK;
881
882         state = (struct idmap_tdb_sids_to_unixids_context *)private_data;
883
884         DEBUG(10, ("idmap_tdb_sids_to_unixids_action: "
885                    " domain: [%s], allocate: %s\n",
886                    state->dom->name,
887                    state->allocate_unmapped ? "yes" : "no"));
888
889         for (i = 0; state->ids[i]; i++) {
890                 if ((state->ids[i]->status == ID_UNKNOWN) ||
891                     /* retry if we could not map in previous run: */
892                     (state->ids[i]->status == ID_UNMAPPED))
893                 {
894                         NTSTATUS ret2;
895
896                         ret2 = idmap_tdb_sid_to_id(state->dom, state->ids[i]);
897                         if (!NT_STATUS_IS_OK(ret2)) {
898
899                                 /* if it is just a failed mapping, continue */
900                                 if (NT_STATUS_EQUAL(ret2, NT_STATUS_NONE_MAPPED)) {
901
902                                         /* make sure it is marked as unmapped */
903                                         state->ids[i]->status = ID_UNMAPPED;
904                                         ret = STATUS_SOME_UNMAPPED;
905                                 } else {
906                                         /* some fatal error occurred, return immediately */
907                                         ret = ret2;
908                                         goto done;
909                                 }
910                         } else {
911                                 /* all ok, id is mapped */
912                                 state->ids[i]->status = ID_MAPPED;
913                         }
914                 }
915
916                 if ((state->ids[i]->status == ID_UNMAPPED) &&
917                     state->allocate_unmapped)
918                 {
919                         ret = idmap_tdb_new_mapping(state->dom, state->ids[i]);
920                         if (!NT_STATUS_IS_OK(ret)) {
921                                 goto done;
922                         }
923                 }
924         }
925
926 done:
927         return ret;
928 }
929
930 static NTSTATUS idmap_tdb_sids_to_unixids(struct idmap_domain *dom, struct id_map **ids)
931 {
932         struct idmap_tdb_context *ctx;
933         NTSTATUS ret;
934         int i;
935         struct idmap_tdb_sids_to_unixids_context state;
936
937         /* initialize the status to avoid suprise */
938         for (i = 0; ids[i]; i++) {
939                 ids[i]->status = ID_UNKNOWN;
940         }
941
942         ctx = talloc_get_type(dom->private_data, struct idmap_tdb_context);
943
944         state.dom = dom;
945         state.ids = ids;
946         state.allocate_unmapped = false;
947
948         ret = idmap_tdb_sids_to_unixids_action(ctx->db, &state);
949
950         if (NT_STATUS_EQUAL(ret, STATUS_SOME_UNMAPPED) && !dom->read_only) {
951                 state.allocate_unmapped = true;
952                 ret = dbwrap_trans_do(ctx->db,
953                                       idmap_tdb_sids_to_unixids_action,
954                                       &state);
955         }
956
957         return ret;
958 }
959
960
961 /**********************************
962  Close the idmap tdb instance
963 **********************************/
964
965 static struct idmap_methods db_methods = {
966         .init = idmap_tdb_db_init,
967         .unixids_to_sids = idmap_tdb_unixids_to_sids,
968         .sids_to_unixids = idmap_tdb_sids_to_unixids,
969         .allocate_id = idmap_tdb_get_new_id,
970 };
971
972 NTSTATUS idmap_tdb_init(void)
973 {
974         DEBUG(10, ("calling idmap_tdb_init\n"));
975
976         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "tdb", &db_methods);
977 }